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

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

问题描述

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

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

但是,我无法访问回调中创建的对象的数据属性。它看起来像

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

但它也有同样的问题。

推荐答案

您应该了解这个



这个(aka上下文)是每个函数中的一个特殊关键字,取决于函数是如何调用的,而不是如何/何时/在哪里被定义。它不受词汇作用域的影响,像其他变量一样。这里有一些例子:

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 scope, like other variables. 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`

关于 this ,请查看 MDN文档

你实际上不想访问 this ,特别是它引用的对象。这就是为什么一个简单的解决方案是简单地创建一个新的变量,也引用该对象。变量可以有任何名称,但常见的是 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 一个正常变量,它遵守词法作用域规则,并且在回调中是可访问的。这也有优点,你可以访问回调本身的 this 值。

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] $ c> this 绑定到一个值。该函数与您调用 .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 behavior 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 behavior they have, please refer to the MDN documentation for more information.

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



设置 this 回调 - 第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

注意: / strong>在该函数/方法的文档中通常提到是否可以传递的这个的值。例如, jQuery的 $。ajax 方法 [docs ] 描述了一个名为上下文的选项:

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.






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

此问题的另一个常见表现是当一个对象方法用作回调/事件处理程序时。函数是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 单击事件处理程序,但如果正文被点击,记录的值将是 undefined ,因为在事件处理程序中, this 指代正文,而不是 Foo 的实例。

如前所述, this 指的是依赖于调用的函数,而不是如何定义

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

The function this.method is assigned as click event handler, but if the body is clicked, the value logged will be undefined, because inside the event handler, this refers to the 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;

解决方案与上述相同:如果可用, code> .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 has 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` / context?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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