如何在回调中访问正确的`this`? [英] How to access the correct `this` inside a callback?
问题描述
我有一个注册事件处理程序的构造函数:
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 $的实例c $ c>。
指的是取决于函数的调用方式 ,而不是如何定义。
正如开头已经提到的那样,这个
如果代码如下所示,可能更明显的是该函数没有对该对象的隐式引用:
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屋!