Extreme Thinking
深入理解JS里的this关键字

2015-04-23


js里的this表示当前对象,如果在全局作用范围内使用this,则指代当前页面对象window, 如果在函数中使用this, 则this指代什么是根据运行时此函数在什么对象上被调用。

函数中的this是在运行时决定的,而不是函数定义时,使用apply/call/bind还可以改变函数中this的指向.

Execution Context

每当JavaScript解释器执行函数的时候,就会产生一个执行上下文。执行上下文其实是一个堆栈,堆栈底部永远是全局上下文,堆栈顶部则是当前活动的上下文。

this表示当前活动的上下文。

函数调用方式与对应的this

this总是指向调用它所在方法的对象,假设有个函数foo(), 不同的调用方式决定了在foo()函数内部的this指针的含义:

  • The Method Invocation Pattern - 函数foo()不属于任何对象,也就是说这是一个全局函数,直接使用foo()方式调用,这时的this是全局对象,也就是window对象(注意: 在strict模式下, 全局函数里的this不再指window对象而是undefined)
  • The Function Invocation Pattern - 函数foo()是对象theObj的成员函数,通过 "theObj.foo()" 方式调用,foo()方法里的this就是theObj
  • The Constructor Invocation Pattern - 通过操作符 new 调用函数,类似 var myFoo = new foo(); 这时foo()方法里的this就是new操作符新创建出来的对象,也就是myFoo
  • The Apply Invocation Pattern - apply/call/bind方式,参考下一节。

改变函数调用时的this

var kid = {
    name: "Alex",
    sayHello: function (age, hobbies) {
        console.log(this.name, 'is', age, 'years old, love', hobbies);
    }
}

var mom = {
    name: "Elaine"
}

kid.sayHello(5, 'sleeping'); // Alex is 5 years old, love sleeping

1) call

kid.sayHello.call(mom, 30, 'reading'); // Elaine is 30 years old, love reading

2) apply

kid.sayHello.apply(mom, [31, 'reading']); // Elaine is 31 years old, love reading

apply与call的区别就是apply将参数放在数组里.

3) bind

var sayHello2 = kid.sayHello.bind(mom);
sayHello2(32, 'reading'); // Elaine is 32 years old, love reading

var sayHello3 = kid.sayHello.bind({name:'Rain'}, 36, 'playing football');
sayHello3(); // Rain is 36 years old, love playing football

bind时除了可以改变this,也可以预置参数,可以预置方法声明时从前往后的一个或多个参数。

bind之后,无论在哪个情况下执行bind后的方法,方法里的this都为bind时的对象。

回调函数里的this

在回调函数里,strict模式下this为undefined,not strict模式下this为window。

以setTimeout()为例,如果要让回调函数得到与setTimeout()方法一样的this,必须要使用语句 var that = this 来绑定当前的context:

var that = this;
setTimeout(function () {
    that.foo();
}, 1000);