我有一个构造函数,它注册一个事件处理程序:
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
并不是指创建的对象,而是指另一个对象 .
我还尝试使用对象方法而不是匿名函数:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', this.alert);
}
MyConstructor.prototype.alert = function() {
alert(this.name);
};
但它表现出同样的问题 .
如何访问正确的对象?
8 回答
你应该了解这一点
this
(又名"the context")是每个函数中的一个特殊关键字,它的值仅取决于函数的调用方式,而不取决于函数的定义方式/时间/位置 . 它不像其他变量那样受词法范围的影响(箭头函数除外,见下文) . 这里有些例子:要了解有关
this
的更多信息,请查看MDN documentation .如何引用正确的
不要使用它
实际上你并不想特别访问
this
,而是它所引用的对象 . 这就是为什么一个简单的解决方案就是简单地创建一个也引用该对象的新变量 . 变量可以有任何名称,但常见的是self
和that
.由于
self
是一个普通变量,它遵循词法范围规则并且可以在回调中访问 . 这样做的另一个好处是,您可以访问回调本身的this
值 .明确设置回调的第一部分 - 第1部分
看起来你可能无法控制
this
的值,因为它的值是自动设置的,但事实并非如此 .每个函数都有.bind [docs]方法,它返回一个新函数,其中
this
绑定到一个值 . 该函数与您调用.bind
的函数具有完全相同的行为,只有this
由您设置 . 无论何时或何时调用该函数,this
将始终引用传递的值 .在这种情况下,我们将回调的
this
绑定到MyConstructor
的this
的值 .Note: 绑定jQuery的上下文时,请改用jQuery.proxy [docs] . 这样做的原因是,在解除对事件回调的绑定时,您不需要存储对该函数的引用 . jQuery在内部处理 .
ECMAScript 6:使用箭头功能
ECMAScript 6引入了箭头函数,可以将其视为lambda函数 . 他们没有自己的
this
绑定 . 相反,this
在范围内被查找,就像一个普通变量一样 . 这意味着您不必调用.bind
. 这不是他们唯一的特殊行为,请参阅MDN文档以获取更多信息 .设置回调 - 第2部分
一些接受回调的函数/方法也接受回调函数
this
应该引用的值 . 这与自己绑定基本相同,但函数/方法为您完成 . Array#map [docs]就是这样一种方法 . 它的签名是:第一个参数是回调,第二个参数是
this
应该引用的值 . 这是一个人为的例子:Note: 是否可以传递
this
的值通常在该函数/方法的文档中提及 . 例如,jQuery's $.ajax method [docs]描述了一个名为context
的选项:常见问题:使用对象方法作为回调/事件处理程序
此问题的另一个常见表现是将对象方法用作回调/事件处理程序 . 函数是JavaScript中的一等公民,术语“方法”只是一个函数的口语术语,它是对象属性的值 . 但是该函数没有与其“包含”对象的特定链接 .
请考虑以下示例:
函数
this.method
被指定为单击事件处理程序,但如果单击document.body
,则记录的值将为undefined
,因为在事件处理程序中,this
指的是document.body
,而不是Foo
的实例 .正如开头已经提到的那样,
this
指的是取决于函数是如何 called ,而不是它是如何 defined .如果代码如下所示,则可能更明显的是该函数没有对该对象的隐式引用:
The solution 与上面提到的相同:如果可用,使用
.bind
将this
显式绑定到特定值或者通过使用匿名函数作为回调/事件处理程序并将对象(
this
)分配给另一个变量,显式地将该函数作为对象的"method"调用:或使用箭头功能:
我们不能将它绑定到
setTimeout()
,因为它总是用 global object (Window) 执行,如果你想在回调函数中访问this
上下文然后使用bind()
到我们可以实现的回调函数:另一种方法是 the standard way since DOM2 在事件中绑定
this
监听器, let you always remove the listener (以及其他好处)是EventListener
接口的handleEvent(evt)
方法:有关使用
handleEvent
的详细信息,请访问:https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38首先,您需要在 scope 的上下文中清楚地了解 scope 和 this 关键字的行为 .
this & scope :
简而言之,全局范围是指窗口对象 . 在全局范围内声明的变量可以从任何地方访问 . 另一方面,函数范围驻留在函数内部 . 函数内部声明的变量通常无法从外部访问 . this 全局范围内的关键字是指窗口对象 . this 内部函数也指向窗口对象 . 所以 this 将始终引用窗口,直到我们找到一种方法来操纵 this 以指示我们自己选择的上下文 .
Different ways to manipulate this inside callback functions:
这里我有一个名为Person的构造函数 . 它有一个名为 name 的属性和四个方法,名为 sayNameVersion1 , sayNameVersion2 , sayNameVersion3 , sayNameVersion4 . 它们中的所有四个都有一个特定的任务 . 接受回调并调用它 . 回调有一个特定的任务,即记录Person构造函数实例的name属性 .
现在让我们从人员构造函数创建一个实例,并使用 niceCallback 调用 sayNameVersionX (X指向1,2,3,4)方法的不同版本,以查看我们可以通过多少种方式操纵 this 内部回调来引用 person 实例 .
bind :
绑定的作用是创建一个将 this 关键字设置为提供值的新函数 .
sayNameVersion1
和sayNameVersion2
使用bind来操纵 this 的回调函数 .第一个绑定 this 与方法本身内部的回调 . 对于第二个回调是通过绑定到它的对象传递 .
call :
call 方法的 first argument 用作 this 在函数内部,该函数在附加 call 的情况下调用 .
sayNameVersion3
使用 call 来操纵 this 来引用我们创建的person对象,而不是window对象 .它被称为如下:
apply :
与 call 类似, apply 的第一个参数指的是由 this 关键字指示的对象 .
sayNameVersion4
使用 apply 来操纵 this 来引用person对象并且它被调用如下 . 简单地说回调是通过的,
以下是在子上下文中访问父上下文的几种方法 -
您可以使用
bind()
功能 .在另一个变量中存储对context / this的引用(参见下面的示例) .
使用ES6 Arrow功能 .
更改代码/功能设计/体系结构 - 为此,您应该在javascript中使用design patterns命令 .
1.使用bind()函数
如果你正在使用
underscore.js
- http://underscorejs.org/#bind2在另一个变量中存储对context / this的引用
3箭头功能
“上下文”的麻烦
术语"context"有时用于指代由此引用的对象 . 它的使用是不合适的,因为它在语义上或技术上都不适合ECMAScript's this .
"Context"表示围绕某些事物增加意义的情况,或者提供额外含义的一些前后信息 . 术语"context"在ECMAScript中用于引用execution context,这是所有参数,范围和这在某些执行代码的范围内 .
这显示在ECMA-262 section 10.4.2中:
这清楚地表明这是执行环境的一部分 .
执行上下文提供周围信息,为正在执行的代码添加含义 . 它包含更多只有thisBinding的信息 .
所以这个值不仅仅是执行上下文的一部分 . 它本质上是一个局部变量,可以通过调用任何对象并在严格模式下设置为任何值 .
目前,如果在代码中使用类,则可以采用另一种方法 .
在class fields的支持下,可以使用下一个方法:
确实在引擎盖下它是绑定上下文的所有旧的好箭头函数,但在这种形式下,它看起来更清楚明确的绑定 .
由于它是第3阶段提案,因此您需要使用babel并使用babel plugin来处理它(08/2018) .
这一切都在调用方法的“神奇”语法中:
当您从对象获取属性并一次调用它时,该对象将是该方法的上下文 . 如果您调用相同的方法,但是在单独的步骤中,则上下文是全局范围(窗口):
当您获得方法的引用时,它不再附加到对象,它只是对普通函数的引用 . 当您获得用作a的引用时,会发生同样的情况打回来:
这就是你将上下文绑定到函数的地方:
如果您使用的是jQuery,则应使用
$.proxy
方法,因为并非所有浏览器都支持bind
: