JavaScript函数中的关键字this是自动获得的,因此,了解在不同的情况下this值指向的对象可以帮助我们减少程序中很多’莫名其妙’的bug.
全局上下文
在全局执行上下文中,this指向全局对象(browser: window, node: global),不管是严格模式还是非严格模式。
函数上下文
在函数中,this值取决与函数是如何调用的。
简单调用(不作为对象的方法或属性)
- 非严格模式下:this值将会默认指向全局对象:
function foo1() {
return this;
}
console.log(foo1() === window); // true
- 严格模式下:this值将会指向进入执行上下文时所设置的值,如果在执行上下文中没有设置this值,则会默认为undefined:
function foo2() {
'use strict';
return this;
}
console.log(foo2() === undefined); // true
作为对象的方法
当函数作为对象的方法或属性时,它的this值将被设为调用该方法的对象:
var o = {
prop: 37,
f() {
return this.prop;
}
};
console.log(o.f()); // 37
即使是通过对象字面量的方法将一个函数赋值给一个对象的属性,该行为也不会发生改变:
var o = {prop: 37};
function getProp() {
return this.prop
}
o.f = getProp;
console.log(o.f()); // 37
对象原型链中的this值
同样的,this仍然指向调用该函数的对象,尽管该函数位于该对象的原型链中:
var o = {f: function() {return this.a + this.b;}};
var p = Object.create(o);
p.a = 1;
p.b = 2;
console.log(p.f()); //3
getter 或 setter 中的this
当函数作为属性的getter或者setter时,其中的this值指向该属性所在的对象:
function sum() {
return this.a + this.b + this.c
}
var o = {
a: 1,
b: 2,
c: 3,
get average() {
return (this.a + this.b + this.c) / 3;
}
};
Object.defineProperty(o, 'sum', {
get: sum, enumerable: true, configurable: true
});
console.log(o.average, o.sum); // 2, 6
call方法 VS apply方法
call方法和apply方法都可以设置函数的this值,两者的第一个参数都是this值所指向的对象,区别在于call方法中的arguments是作为单个值传入fun.call(thisArg, arg1, arg2, ...)
,而apply方法中的arguments是作为数组传入func.apply(thisArg, [argsArray])
。
bind方法
ES5引入了Function.prototype.bind方法,调用f.bind(someObject)可以创建一个新的函数,该函数与f具有相同的函数体和作用域,但是新函数this值始终指向someObject。
function f() {
return this.a;
}
var g = f.bind({a: 'abc'});
console.log(g()); // abc
var h = g.bind({b: '123'}); // bind only works once
console.log(h()); //abc
注意到:通过bind方法获得的函数再次调用bind方法得到的新函数this值不会发生变化!
箭头函数
由于this值是自动获得的,以下hack的写法大家一定不陌生:
{
...
addAll: function addAll(pieces) {
var self = this;
_.each(pieces, function (piece) {
self.add(piece);
});
},
...
}
为了解决该问题,ES2015中引入了箭头函数语法糖,然而箭头函数的this值与一般函数的this值又有所区别,箭头函数的this值始终继承其外围作用域中的this值,在全局环境中将指向全局对象:
// ES6
{
...
addAll: function addAll(pieces) {
_.each(pieces, piece => this.add(piece));
},
...
}
此处的addAll方法中的this值指向调用该方法的对象,而其内部的箭头函数的this值则继承自addAll方法的this值。