详解JS里 this 的指向

124 阅读2分钟

首先我们要明确一点this是在函数运行时才确定其指向的,在函数执行上下文中的另一个属性原型链是在函数定义时就确定的。this只取决于函数的调用方式。 我们通过几个例子来解析一下this是如何确定的。

  1. 函数调用位置在全局上下文
function foo() {
console.log( this.a );
}
var a = 2;
foo(); //2

2.函数调用时,是否是通过对象属性调用的,或者说是否被某个对象拥有或者包 含,不过这种说法可能会造成一些误导。这时this执行调用函数的对象,即obj对象

var a = 10;
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2
对象属性引用链中只有最顶层或者说最后一层会影响调用位置。举例来说:
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42

3.构造函数里的this指向新建的对象

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

4.箭头函数的this,与普通函数不同,它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值。它最大的好处,解决了匿名函数的this指向问题,有利于封装回调函数。

普通函数:
var name = 'window'; // 其实是window.name = 'window'

var A = {
   name: 'A',
   sayHello: function(){
      console.log(this.name)
   }
}

A.sayHello();// 输出A

var B = {
  name: 'B'
}

A.sayHello.call(B);//输出B

A.sayHello.call();//不传参数指向全局window对象,输出window.name也就是window
箭头函数:
例子一:
var name = 'window'; 

var A = {
   name: 'A',
   sayHello: () => {
      console.log(this.name)
   }
}

A.sayHello();// 还是以为输出A ? 错啦,其实输出的是window

例子二:
var obj = { 
i: 10, 
b: () => console.log(this.i, this), 
c: function() { 
console.log( this.i, this) 
  } 
} 
obj.b(); // undefined window{...} 
obj.c(); // 10 Object {...}

例子三:
function Person() {
  this.age = 0;
  setTimeout(function() {
    console.log(this);
  });
}

var p = new Person();// window 对象

5.其它特殊情况this的判断,闭包里的调用obj.t2(),因为闭包并不属于这个对象的属性或方法。所以在闭包中的this是指向window的

var a = 10
function fn() {
  return this.a
}

var obj = {
  a: 5,
  t1: fn, // obj.t1() 结果是5
  t2: function() {
    return fn()
  },//obj.t2() 结果是 10
  t3: function() {
    return this.a
  },// obj.t3() 结果是5
  t4: () => this.a,  //obj.t4() 结果是10 普通this是哪个对象最近调用的是哪个,箭头函数的编译时就定义好的。
}

函数赋值的情况也会导致this指向变化:

function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性
bar(); // "oops, global"