如何在回调中访问正确的`this`? [英] How to access the correct `this` inside a callback?

查看:207
本文介绍了如何在回调中访问正确的`this`?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个注册事件处理程序的构造函数:

I have a constructor function which registers an event handler:

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 属性。它看起来像这个没有引用创建的对象而是引用另一个对象。

However, I'm not able to access the data property of the created object inside the callback. It looks like this does not refer to the object that was created but to an other one.

我也尝试过使用对象方法而不是匿名函数:

I also tried to use an object method instead of an anonymous function:

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

MyConstructor.prototype.alert = function() {
    alert(this.name);
};

但它表现出同样的问题。

but it exhibits the same problems.

我如何访问正确的对象?

How can I access the correct object?

推荐答案

你应该了解这个



(又名上下文)是每个函数中的特殊关键字,仅限其值取决于如何调用函数,而不是如何/何时/何地定义它。它不像其他变量那样受词法范围的影响(箭头函数除外,见下文)。以下是一些示例:

What you should know about this

this (aka "the context") is a special keyword inside each function and its value only depends on how the function was called, not how/when/where it was defined. It is not affected by lexical scopes like other variables (except for arrow functions, see below). Here are some examples:

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文档

To learn more about this, have a look at the MDN documentation.

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

You actually don't want to access this in particular, but the object it refers to. That's why an easy solution is to simply create a new variable that also refers to that object. The variable can have any name, but common ones are self and that.

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

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

Since self is a normal variable, it obeys lexical scope rules and is accessible inside the callback. This also has the advantage that you can access the this value of the callback itself.

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

It might look like you have no control over the value of this because its value is set automatically, but that is actually not the case.

每个函数都有 .bind [docs] ,返回一个新的函数与这个绑定到一个值。该函数与您调用 .bind 的函数完全相同,只有由您设置。无论该函数如何或何时被调用,将始终引用传递的值。

Every function has the method .bind [docs], which returns a new function with this bound to a value. The function has exactly the same behaviour as the one you called .bind on, only that this was set by you. No matter how or when that function is called, this will always refer to the passed value.

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);
}

在这种情况下,我们绑定回调的这个的值为 MyConstructor

In this case, we are binding the callback's this to the value of MyConstructor's this.

注意:在为jQuery绑定上下文时,请使用 jQuery.proxy [docs] 。这样做的原因是,在解除对事件回调的绑定时,您不需要存储对该函数的引用。 jQuery在内部处理。

Note: When binding context for jQuery, use jQuery.proxy [docs] instead. The reason to do this is so that you don't need to store the reference to the function when unbinding an event callback. jQuery handles that internally.

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

ECMAScript 6 introduces arrow functions, which can be thought of as lambda functions. They don't have their own this binding. Instead, this is looked up in scope just like a normal variable. That means you don't have to call .bind. That's not the only special behaviour they have, please refer to the MDN documentation for more information.

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



设置回调 - 第2部分



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

Set this of the callback - part 2

Some functions/methods which accept callbacks also accept a value to which the callback's this should refer to. This is basically the same as binding it yourself, but the function/method does it for you. Array#map [docs] is such a method. Its signature is:

array.map(callback[, thisArg])

第一个参数是回调,第二个参数是这个应该引用的值。这是一个人为的例子:

The first argument is the callback and the second argument is the value this should refer to. Here is a contrived example:

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 方法 [docs] 描述了一个名为 context 的选项:

Note: Whether or not you can pass a value for this is usually mentioned in the documentation of that function/method. For example, jQuery's $.ajax method [docs] describes an option called context:

此对象将成为所有与Ajax相关的回调的上下文。

This object will be made the context of all Ajax-related callbacks.






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



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


Common problem: Using object methods as callbacks/event handlers

Another common manifestation of this problem is when an object method is used as callback/event handler. Functions are first-class citizens in JavaScript and the term "method" is just a colloquial term for a function that is a value of an object property. But that function doesn't have a specific link to its "containing" object.

请考虑以下示例:

function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = function() {
    console.log(this.data);
};

函数 this.method 被指定为单击事件处理程序,但如果单击 document.body ,则记录的值将为 undefined ,因为在事件内部handler, this 引用 document.body ,而不是 Foo

正如开头已经提到的那样,这个
指的是取决于函数的调用方式 ,而不是如何定义

如果代码如下所示,可能更明显的是该函数没有对该对象的隐式引用:

The function this.method is assigned as click event handler, but if the document.body is clicked, the value logged will be undefined, because inside the event handler, this refers to the document.body, not the instance of Foo.
As already mentioned at the beginning, what this refers to depends on how the function is called, not how it is defined.
If the code was like the following, it might be more obvious that the function doesn't have an implicit reference to the object:

function method() {
    console.log(this.data);
}


function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = method;

解决方案与上述相同:如果可用,请使用 .bind 显式绑定到特定值

The solution is the same as mentioned above: If available, use .bind to explicitly bind this to a specific value

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

或通过使用匿名函数作为回调显式调用该函数作为对象的方法 / event handler并将对象( this )分配给另一个变量:

or explicitly call the function as a "method" of the object, by using an anonymous function as callback / event handler and assign the object (this) to another variable:

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

或使用箭头功能:

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

这篇关于如何在回调中访问正确的`this`?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆