如何在回调中访问正确的`this`?


如何在回调中访问正确的this


你应该知道什么 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`

如何参考正确 this

不要用 this

您实际上并不想this特别访问它,而是它所引用的对象。这就是为什么一个简单的解决方案就是简单地创建一个也引用该对象的新变量。变量可以有任何名称,但常见的是self和that。

function MyConstructor(data, transport) {
    this.data = data;
    var self = this;
    transport.on('data', function() {
        alert(self.data);
    });
}

由于它self是一个普通变量,它遵循词法范围规则,并且可以在回调中访问。这也有一个优点,您可以访问this回调本身的值。

明确设置this回调 - 第1部分

看起来您可能无法控制其值,this因为它的值是自动设置的,但事实并非如此。

每个函数都有方法.bind [docs],它返回一个this绑定到值的新函数。该函数与您调用的函数具有完全相同的行为.bind,仅this由您设置。无论何时或何时调用该函数,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's 的值this。

ECMAScript 6:使用箭头功能

ECMAScript 6引入了箭头函数,可以将其视为lambda函数。他们没有自己的this约束力。相反,this就像普通变量一样在范围内查找。这意味着你不必打电话.bind。这不是他们唯一的特殊行为,请参阅MDN文档以获取更多信息。

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', () => alert(this.data));
}

设置this回调-第2部分

一些接受回调的函数/方法也接受回调this应该引用的值。这与自己绑定基本相同,但函数/方法为您完成。Array#map [docs]就是这样一种方法。它的签名是:

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

常见问题:使用对象方法作为回调/事件处理程序

此问题的另一个常见表现是将对象方法用作回调/事件处理程序。函数是JavaScript中的一等公民,术语“方法”只是一个函数的口语术语,它是对象属性的值。但是该函数没有与其“包含”对象的特定链接。

请考虑以下示例:

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显式绑定this到特定值

document.body.onclick = this.method.bind(this);

或者通过使用匿名函数作为回调/事件处理程序并将object(this)分配给另一个变量,显式地将该函数作为对象的“方法”调用:

var self = this;
document.body.onclick = function() {
    self.method();
};

或使用箭头功能:

document.body.onclick = () => this.method();