对JavaScript中this的理解

在javascript中我们总是会遇到各种各样的this,由于this是在代码运行期确定的,它可以是全局对象、当前对象或者任意对象,这完全取决于this的调用方式。《javascript语言精粹》一书中将this的调用方式总结为以下四种:

  • 作为对象方法调用
  • 作为函数调用
  • 作为构造函数调用
  • apply或call 调用

在补充2点:

  • ES5中引入的bind调用
  • ES6中箭头函数里的this

下面我们将按照调用方式的不同,分别讨论 this 的含义。

作为对象方法调用

当一个函数被保存为对象的一个属性时,我们称它为一个方法。方法被调用时,this被绑定到该对象。通过this可取得所属对象的公有方法

var myObject = {
    value:0,
    increment:function(inc){
        this.value += typeof inc === 'number' ? inc : 1;
        console.log(this);  //{value:1,increment:function(){}}
    }
}
myObject.increment();  //作为对象方法调用

作为函数调用

当一个函数并非一个对象的属性时,那么它就是被当做一个函数来调用的,以此模式调用函数时,this被绑定到全局对象。

var myObject = {
    value : 0,
    double : function(){
        var helper = function(){
            console.log(this); //window
        };
        helper();  //作为函数调用
    }
}

作为构造函数调用

如果一个函数前面带上new来调用,那么背地里将会创建一个连接到该函数的prototype成员的新对象,同时this会被绑定到那个新对象上。

function Quo(string){
    this.status = string;
}
Quo.prototype.getStatus = function(){
    return this;
}
var myQuo = new Quo("confused");
console.log(myQuo.getStatus()); //作为构造函数调用 this指向这个新对象Quo {status: "confused"}

apply或call 调用

apply方法允许我们构建一个函数数组传递给调用函数,也允许选择this的值

var statusObject = {
    status: "ok"
};
function Quo(string){
    this.status = string;
}
Quo.prototype.getStatus = function(){
    return console.log(this); //{status: "ok"}
}
Quo.prototype.getStatus.apply(statusObject); //将方法中的this指向传入的对象
Quo.prototype.getStatus.call(statusObject); //将方法中的this指向传入的对象

通过bind调用

bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。
fun.bind(thisArg[, arg1[, arg2[, ...]]])
** 返回由指定的this值和初始化参数改造的原函数拷贝**

window.color = "red";
var o = {color:"blue"};
function sayColor(){
  return this.color;
}
var objectSayColor = sayColor.bind(o); //此处将方法中的this指向了o对象
objectSayColor();//由于bind返回的是改造后的原函数的拷贝,并没有执行

箭头函数里的this

函数体内的 this 对象,是定义时所在的对象,而不是使用时所在的对象。

var handler = {
  id: '123456',
  init: function() {
    document.addEventListener('click',event => this.doSomething(event.type), false);
  },
  doSomething: function(type) {
    console.log('Handling ' + type + ' for ' + this.id);
  }
};

上面代码的 init 方法中,使用了箭头函数,这导致这个箭头函数里面的 this ,总是指向 handler 对象。否则,回调函数运行时, this.doSomething 这一行会报错,因为此时 this 指向 document 对象。
this 指向的固定化,并不是因为箭头函数内部有绑定 this 的机制,实际原因是箭头函数根本没有自己的 this ,导致内部的 this 就是外层代码块
的 this 。正是因为它没有 this ,所以也就不能用作构造函数。