如何在回调中访问正确的“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
属性.看起来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
,而是它引用的对象.这就是为什么一个简单的解决方案是简单地创建一个也引用该对象的新变量.变量可以有任何名称,但常见的是self
和that
.
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
绑定到 MyConstructor
的 this
的值.
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;
解决方案与上面提到的相同:如果可用,使用.bind
将this
显式绑定到特定值
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屋!