如何在回调中访问正确的this?
我创建了一个构造函数,并且给它绑定了一个事件处理函数:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', function () {
alert(this.data);
});
}
// Mock transport object
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// called as
var obj = new MyConstructor('foo', transport);
然鹅,如上面的代码所示,在回调函数中,我访问不到所创建对象的data属性。
this
指向的不是已创建的这个对象,而是指向别的。
我还尝试使用一个对象方法来代替匿名函数:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', this.alert);
}
MyConstructor.prototype.alert = function() {
alert(this.name);
};
还是不行。
那么,如何通过this
访问到正确的对象?
以下答案仅供参考
this
(又被称为上下文)是每个函数内部的一个特殊关键字,它的值只取决于函数是如何被调用的。
与它如何被定义、何时定义、在哪里定义没有关系。
它不像其他变量那样受词法作用域的影响(箭头函数除外,见下文)。
以下是一些例子:
function foo() {
console.log(this);
}
// normal function call
foo(); // `this` will refer to `window`
// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`
// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`
要了解更多这方面的信息,请查看MDN文档。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
使用箭头函数
ECMAScript 6引入了箭头函数,可以将其视为lambda函数。
箭头函数没有自己的this
。
相反,this
像普通变量一样在作用域中查找。所以没有必要调用.bind
。更多相关信息请参考MDN文档。
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}
实际上你真正想访问的并不是this
,而是但它所指向的对象。
所以有一个简单的解决方案:创建一个新的变量指向你想要访问的对象。这个新变量可以有任何名称,但常见的名称是self
和that
。
例如下面的代码:
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
因为self
是一个普通变量,所以它遵守词法作用域的规则,并且在回调中是可访问的。
这样做还有一个好处,那就是可以访问回调本身的this
值。
虽然this
的值是自动设置的,但也并非是不能控制的。
每个函数都有.bind
方法 查看详细,它返回一个新函数,并将这个函数绑定到一个值上。
这个函数与你调用的.bind函数的行为完全相同,只不过它是由你设置的。
无论如何或何时调用该函数,this
将始终引用所传递的值。
查看如下代码:
function MyConstructor(data, transport) {
this.data = data;
var boundFunction = (function() { // parenthesis are not necessary
alert(this.data); // but might improve readability
}).bind(this); // <- here we are calling `.bind()`
transport.on('data', boundFunction);
}
在这个例子中,我们把回调函数的this
绑定到MyConstructor
的this
的值上。
注意:当为jQuery创建绑定上下文时,请使用jQuery.proxy查看更多。
这样做的原因是,在解除回调函数的绑定时,不需要存储对函数的引用。jQuery自己会在内部处理。
设置this
回调函数 - part 2
一些函数和方法在接收回调函数的同时,也接收这个回调函数的this
应该指向的值。
这跟自己实现绑定的效果基本相同,只不过这些函数和方法替你做了这部分工作。
Array#map
查看更多就是这样一个方法。
它的用法是这样的:
array.map(callback[, thisArg])
第一个参数是回调函数,第二个参数是this
应该引用的值。
看下面的例子:
var arr = [1, 2, 3];
var obj = {multiplier: 42};
var new_arr = arr.map(function(v) {
return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument
注意: 是否可以给这个函数传递一个值,通常在该函数/方法的文档中有所提及。
例如,jQuery的$.ajax
方法文档描述了一个叫做context
的选项:
这个对象将成为所有与ajax相关的回调的上下文。
该问题的另一种常见表现形式是将对象方法用作回调/事件处理程序。
== Functions是JavaScript中的头等公民==,“method”只是一个通俗的术语,表示函数是对象属性的值。
但是这个函数没有"链接"到它“包含”的对象。
思考下面的例子:
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = function() {
console.log(this.data);
};
this.method
被作为点击事件的处理程序。
但是如果document.body
被点击了,被打印出来的值却是undefined
。
因为在这里,this
指向的是document.body
,而不是Foo
的实例。
正如前面所提到的,this
指的是到底是什么,取决于函数是如何被调用的,而不是函数是如何定义的。
请思考下面的代码:
function method() {
console.log(this.data);
}
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = method;
解决方案: 使用.bind
来显式地将它绑定到一个特定的值。
document.body.onclick = this.method.bind(this);
或者是将对象(this
)绑定到另一个变量上:
var self = this;
document.body.onclick = function() {
self.method();
};
或者使用箭头函数:
document.body.onclick = () => this.method();