关于this

为什么我们要使用this:使函数可以自动引用合适的上下文对象

关于this的两个误区:

  1.this指向函数自身,

  2.this指向他的作用域(在某些情况下它是对的,在某些情况下它是错的)

需要注意的是this在任何情况下都不指向函数的词法作用域(词法作用域:一个变量和函数的词法作用域取决于该变量和函数声明的地方)。

this实际上是在函数被调用时所发生的绑定,它指向什么完全取决于函数在哪里调用

以下是总结的一些绑定规则:

1.默认绑定:

  当无法适从其他规则时,是this的默认规则

function bar(){
   console.log(this.a);  
}
var a = 2;
bar();//2

函数调用时,应用了this的默认绑定,this指向了全局变量。为什么this指向了全局?

该函数的调用位置可以理解为在全局调用,所以this指向了全局。当然,如果使用了严格模式,全局对象将无法使用默认绑定,bar()报错。

2.隐式绑定:

应用前提:调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含

例如:

function foo() {
    console.log( this.a ); 
}
var obj = { 
    a: 2, 
    foo: foo 
};
obj.foo(); // 2 

此时的foo()的调用位置实际上是在obj内部,所以this指向了obj

对象属性引用链中只有最顶层或者说最后一层会影响调用位置

function bar(){
  console.log( this.a )  
};
var obj2 = {
  a:42,
  bar:bar  
}
var obj1 = {
  a: 2,
  obj2:obj2
}
obj1.obj2.bar(); //42,指向obj2

需要注意的是,被隐式绑定的函数在某些情况下会丢失绑定对象,也就是说它会应用默认绑定

function foo() { 
  console.log( this.a );
}
var obj = {
  a: 2,
  foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性 
bar(); // "oops, global"
function foo() {
  console.log( this.a ); 
}
function doFoo(fn) {
  // fn 其实引用的是 foo 
  fn(); // <-- 调用位置! 
}
var obj = {
  a: 2,
  foo: foo 
};
var a = "oops, global"; // a 是全局对象的属性
doFoo( obj.foo ); // "oops, global"
例一中虽然 bar 是 obj.foo 的一个引用,但是实际上,它引用的是 foo 函数本身,因此此时的bar() 其实是一个不带任何修饰的函数调用,因此应用了默认绑定
例二中的参数传递就是一种隐式赋值
总结:主要就是看调用的函数的调用位置,他的位置在哪,this指向的就是哪。

3.显示绑定:

可以使用函数的apply(..)和call(..)方法,强制绑定this的值

例如:

function foo() {
  console.log( this.a ); 
}
var obj = {
  a:2 
};
foo.call( obj ); // 2

A.call(B)或者A.apply(B)   临时绑定,对于this绑定来说,他们没有不同。

其他方面的应用,他们有所不同,例如:

     A.call(B,1000,2000,3000):用B调用A函数,并将其后的值传入A函数中

     A.apply(B,arr):用B调用A函数,并将arr数组内的值传入A函数中,自动打散数组为单个值

同时,还有一个bind(),也可以用来进行绑定。

bind()不同于apply(),call()的地方在于:bind是永久绑定,其返回了一个修改后的函数,让对应的函数想什么时候调就什么时候调用,并且可以将参数在执行的时候添加。

例如:

var a = {
    user:"追梦子",
    fn:function(){
        console.log(this.user); //追梦子
    }
}
var b = a.fn;
b.bind(a);//并没有执行,
var c = b.bind(a);
console.log(c); // function() { [native code] }
c(); //此时执行,

4.new绑定:

首先我们要知道,使用new来构造函数时发生了什么:

  1. 创建对象,创建存储空间
  2. 让子对象继承父对象
  3. 调用构造函数并将构造函数中的this指向新对象
  4. 返回新对象的地址给变量

所以其第三步就可以进行this的绑定,例如:

function bar(a){
  this.a = a;
}
var  b = new bar(2);
console.log(b.a); // 2
使用 new 来调用 bar(..) 时,我们会构造一个新对象并把它绑定到 bar(..) 调用中的 this上.
 
对于this绑定的四项规则,他们的优先级如下:
  1.显示绑定优先级高于隐式绑定,
  2.new绑定优先级高于隐式绑定,
 

所以我们可以根据以下来判断this的绑定

  1.函数是否在new中调用(new绑定),如果是的话,this绑定的是新创建的对象

    var  bar = new foo();

  2.函数是否通过apply()或call()显示绑定,如果是的话,this绑定的是指定的对象

    var bar = foo.call(obj2);

  3.函数是否在某个上下文对象中调用(隐式绑定),如果是的话,this绑定的是那个上下文对象

    var bar = obj1.foo();

  4.如果都不是的话,使用默认绑定,如果在严格模式下,就绑定到undefined,否则绑定到全局对象

    var bar = foo();

 

但是,凡是都有例外,对于箭头函数来说,我们不能根据以上四点来判断this的绑定,

箭头函数中,this是根据外层(函数或者全局)作用域来决定的。

例如:

function foo() {
  // 返回一个箭头函数
  return (a) => {
    //this 继承自 foo() 
    console.log( this.a );
  }; 
}
var obj1 = {
  a:2 
};
var obj2 = {
  a:3
};
var bar = foo.call( obj1 );
bar.call( obj2 ); // 2, 不是 3 !
foo() 内部创建的箭头函数会捕获调用时 foo() 的 this。由于 foo() 的 this 绑定到 obj1,bar(引用箭头函数)的 this 也会绑定到 obj1,箭头函数的绑定无法被修改。
1.箭头函数没有自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象),而不是执行时的对象,
2.箭头函数中,this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。
例如:
function Person (name,age){
    this.name = name;
    this.age = age;
    setInterval(()=>{
        console.log("I'm " + this.name + " and I'm " + this.age +" years old");
    },100)
}
var lilei = Person('LiLei',20);//I'm LiLei and I'm 20 years old

this指向的是Person对象

 

posted @ 2019-11-27 22:13  whale~alince  阅读(162)  评论(0编辑  收藏  举报