你真的知道this吗?

107 阅读2分钟

MDN中的this定义

当前执行代码的环境对象。

多么简约凝练的概括,寥寥11个字,将伴随程序员的前世今生。话不多说,让我们一起探讨你不知道的this

牛刀小试

  function foo() {
      console.log( this.a );
  }
  var a = 10;
  foo();

答案: 10 this —> window

  function foo() { 
      console.log( this.a );
  }
  var obj2 = { 
      a: 42,
      foo: foo 
  };
  var obj1 = { 
      a: 2,
      obj2: obj2 
  };
  obj1.obj2.foo(); 

42 this -> obj2

初露锋芒

  function foo() {
  	setTimeout(() => this.a = 1, 0)
  	console.log( this.a );
  }
  function foo2() {
  	setTimeout(() => this.a = 1, 500)
  	console.log( this.a );
  }
  function doFoo(fn) {
  	this.a = 4
  	fn();
  }
  var obj = {
  	a: 2,
  	foo: foo,
  	foo2: foo2
  };
  var a =3
  doFoo( obj.foo );
  setTimeout( obj.foo, 0 )
  setTimeout( obj.foo2, 100 )

答案: 4 1 1 foo函数,第2行是一个定时器,哪怕定时器的延迟时间为0,仍然先执行第3行。故为4 当使用定时器调用函数时,先执行函数内代码,在进行函数调用。故为1 同理: 故为1

展露头脚

a = 3
function foo() {
	console.log( this.a );
}
var obj = {
	a: 2
};
foo.call( obj ); 
foo.call( null );
foo.call( undefined ); 
foo.call(  ); 
var obj2 = {
	a: 5,
	foo
}
obj2.foo.call() // 3,不是5!
//bind返回一个新的函数
function foo(something) {
	console.log( this.a, something );
	return this.a + something;
}
var obj = {
	a: 2
}
var bar = foo.bind( obj );
var b = bar( 3 ); 
console.log( b );

答案: 2 undefined 3 undefined 3 undefined 3 undefined 3 undefined 2 3 5 第8行: this -> obj = 2, msg没有传值,故为undefined 第9~12行: 当call的第一个参数为null, undefined或者不传值时,只想window, this -> window = 3, msg没有传值,故为undefined 第25行:bind改变指向 -> obj,第26行:msg为3, 故为2 3 第27行:执行函数,为2+3 = 5

锐不可当

隐式丢失

一个最常见的 this绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把 this 绑定到全局对象或者 undefined 上,取决于是否是严格模式

function foo() { 
    console.log( this.a );
}
function doFoo(fn) {
    fn()
}
var obj = {
    a: 2,
    foo: foo 
};
var a = "oops, global";
doFoo( obj.foo ); 

答案: oops, global 第5行fn()引用第位置其实foo, 因此doFoo()相当于是一个不带修饰符的函数调用,因此应用了默认绑定—> window = oops, global

舍我其谁

function foo(something) { 
    this.a = something;
}
var obj1 = { 
    foo: foo
};
var obj2 = {};
obj1.foo( 2 );
console.log( obj1.a );
obj1.foo.call( obj2, 3 );
console.log( obj2.a );
var bar = new obj1.foo( 4 ); 
console.log( obj1.a );

答案: 2 3 2 4 new 绑定比隐式绑定优先级高,也闭隐式绑定绑定优先级高

总结

  1. 函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象。 var bar = new foo()
  2. 函数是否通过call、apply(显式绑定)或者硬绑定调用?如果是的话,this绑定的是 指定的对象。 var bar = foo.call(obj2)
  3. 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this 绑定的是那个上 下文对象。 var bar = obj1.foo()
  4. 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到 全局对象。 var bar = foo()