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

查看:39
本文介绍了如何在回调中访问正确的“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 属性.看起来this 不是指创建的对象,而是指另一个.

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 another 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.

如何访问正确的对象?

推荐答案

你应该知道的关于this

this(又名上下文")是每个函数内部的一个特殊关键字,其值仅取决于如何调用该函数,而不是如何/何时/它被定义的地方.它不像其他变量那样受词法作用域的影响(箭头函数除外,见下文).以下是一些示例:

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`

要了解有关 this 的更多信息,请查看 MDN 文档.

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

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

ECMAScript 6 introduced 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

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

Don't use this

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.

您可能无法控制 this 的值,因为它的值是自动设置的,但实际上并非如此.

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],它返回一个新函数,其中 this 绑定到一个值.该函数与您调用 .bind 的函数具有完全相同的行为,只是 this 是由您设置的.无论该函数如何或何时被调用,this 将始终引用传递的值.

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

在这种情况下,我们将回调的 this 绑定到 MyConstructorthis 的值.

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

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

Note: When a 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.

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

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

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

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

注意:是否可以为 this 传递值通常会在该函数/方法的文档中提及.例如,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 中的一等公民,术语方法"是 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,因为在事件处理程序中,this 指的是document.body,而不是Foo 的实例.
正如开头已经提到的,this 指的是什么取决于函数如何调用,而不是它如何定义.
如果代码如下所示,可能更明显的是该函数没有对对象的隐式引用:

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;

解决方案与上面提到的相同:如果可用,使用.bindthis显式绑定到特定值

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

或显式调用函数作为方法";对象,通过使用匿名函数作为回调/事件处理程序并将对象 (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天全站免登陆