提问人:Felix Kling 提问时间:11/29/2013 最后编辑:Peter MortensenFelix Kling 更新时间:4/12/2023 访问量:619723
如何在回调中访问正确的“this”
How to access the correct `this` inside a callback
问:
我有一个注册事件处理程序的构造函数:
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);
};
但它表现出同样的问题。
如何访问正确的对象?
答:
你应该知道什么this
this
(又名“上下文”)是每个函数中的一个特殊关键字,其值仅取决于函数的调用方式,而不取决于函数的定义方式/时间/位置。它不像其他变量那样受到词法范围的影响(箭头函数除外,见下文)。以下是一些示例:
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 文档。this
如何参考正确的this
使用箭头函数
ECMAScript 6 引入了箭头函数,可以将其视为 lambda 函数。它们没有自己的绑定。相反,在作用域中查找,就像普通变量一样。这意味着您不必致电 .这不是他们唯一的特殊行为,请参考 MDN 文档了解更多信息。this
this
.bind
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}
请勿使用this
您实际上不想特别访问它所指的对象。这就是为什么一个简单的解决方案是简单地创建一个也引用该对象的新变量。变量可以具有任何名称,但常见的名称是 和 。this
self
that
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
由于是一个普通变量,因此它遵循词法范围规则,并且可以在回调中访问。这样做还有一个优点,即您可以访问回调本身的值。self
this
显式设置回调 - 第 1 部分this
看起来您无法控制 的值,因为它的值是自动设置的,但实际上并非如此。this
每个函数都有 .bind
[docs] 方法,该方法返回一个绑定到值的新函数。该函数的行为与您调用的函数完全相同,只是该函数是由您设置的。无论以何种方式或何时调用该函数,都将始终引用传递的值。this
.bind
this
this
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
注意:当 jQuery 的绑定上下文时,请改用 jQuery.proxy
[docs]。这样做的原因是,在取消绑定事件回调时,不需要存储对函数的引用。jQuery在内部处理。
回调集 - 第 2 部分this
一些接受回调的函数/方法也接受回调应引用的值。这基本上和自己绑定它一样,但函数/方法会为你做这件事。Array#map
[docs] 就是这样一种方法。其签名是:this
array.map(callback[, thisArg])
第一个参数是回调,第二个参数是应该引用的值。下面是一个人为的例子:this
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] 描述了一个名为 :this
context
此对象将成为所有与 Ajax 相关的回调的上下文。
常见问题:使用对象方法作为回调/事件处理程序
此问题的另一个常见表现是将对象方法用作回调/事件处理程序时。函数是 JavaScript 中的一等公民,术语“方法”只是作为对象属性值的函数的口语术语。但该函数没有指向其“包含”对象的特定链接。
请看以下示例:
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
function method() {
console.log(this.data);
}
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = method;
解决方案与上面提到的相同:如果可用,则用于显式绑定到特定值.bind
this
document.body.onclick = this.method.bind(this);
或者使用匿名函数作为回调/事件处理程序,将该函数作为对象的“方法”显式调用,并将对象 () 分配给另一个变量:this
var self = this;
document.body.onclick = function() {
self.method();
};
或使用箭头函数:
document.body.onclick = () => this.method();
评论
self
that
this
this
self
that
_this
$(...).on('click', $.proxy(obj, 'function'))
$(...).off('click', obj.function)
Function.prototype.call ()
Function.prototype.apply ()
apply ()
bind ()
bind()
bind()
this
bind()
这一切都在调用方法的“神奇”语法中:
object.property();
当您从对象中获取属性并一次性调用它时,该对象将成为该方法的上下文。如果调用相同的方法,但在单独的步骤中,则上下文是全局范围(窗口):
var f = object.property;
f();
获取方法的引用后,它不再附加到对象。它只是对普通函数的引用。当您获取要用作回调的引用时,也会发生同样的情况:
this.saveNextLevelData(this.setAll);
这就是将上下文绑定到函数的位置:
this.saveNextLevelData(this.setAll.bind(this));
如果您使用的是 jQuery,则应改用该方法,因为并非所有浏览器都支持该方法:$.proxy
bind
this.saveNextLevelData($.proxy(this.setAll, this));
“上下文”的麻烦
术语“上下文”有时用于指代 this 引用的对象。它的使用是不恰当的,因为它在语义上或技术上都不符合 ECMAScript 的 this。
“上下文”是指围绕事物增加意义的情况,或一些赋予额外意义的前后信息。术语“上下文”在 ECMAScript 中用于指代执行上下文,即所有参数、作用域,以及某些执行代码范围内的参数。
这在 ECMA-262 第 10.4.2 节中显示:
将 ThisBinding 设置为与 ThisBinding 相同的值 调用执行上下文
这清楚地表明这是执行上下文的一部分。
执行上下文提供周围信息,为正在执行的代码添加意义。它包含的信息比 thisBinding 多得多。
它的价值不是“上下文”。它只是执行上下文的一部分。它本质上是一个局部变量,可以通过调用任何对象来设置,在严格模式下,可以设置为任何值。
评论
this
以下是访问子上下文中的父上下文的几种方法:
- 您可以使用该函数。
bind()
- 将对 context/this 的引用存储在另一个变量中(请参阅以下示例)。
- 使用 ES6 箭头函数。
- 更改代码、函数设计和架构 - 为此,您应该能够控制 JavaScript 中的设计模式。
1. 使用功能bind()
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', ( function () {
alert(this.data);
}).bind(this) );
}
// Mock transport object
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// called as
var obj = new MyConstructor('foo', transport);
如果您使用的是下划线.js - http://underscorejs.org/#bind
transport.on('data', _.bind(function () {
alert(this.data);
}, this));
2. 将对 context/this 的引用存储在另一个变量中
function MyConstructor(data, transport) {
var self = this;
this.data = data;
transport.on('data', function() {
alert(self.data);
});
}
3.箭头功能
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
评论
首先,您需要清楚地了解 this
关键字在 scope
上下文中的范围
和行为。
这个
范围
(&):
JavaScript 中有两种类型的作用域。他们是:
全球范围
功能范围
简而言之,全局范围是指窗口对象。在全局范围内声明的变量可以从任何位置访问。
另一方面,函数作用域位于函数内部。函数内部声明的变量无法正常从外部世界访问。
全局范围内的 this
关键字是指 window 对象。函数内部的这也
指 window 对象。因此,这将始终引用窗口,
直到我们找到一种方法来操作它
以指示我们自己选择的上下文。
--------------------------------------------------------------------------------
- -
- Global Scope -
- (globally "this" refers to window object) -
- -
- function outer_function(callback){ -
- -
- // Outer function scope -
- // Inside the outer function, the "this" keyword -
- // refers to window object -
- callback() // "this" inside callback also refers to the window object -
- } -
- -
- function callback_function(){ -
- -
- // Function to be passed as callback -
- -
- // Here "THIS" refers to the window object also -
- } -
- -
- outer_function(callback_function) -
- // Invoke with callback -
- -
--------------------------------------------------------------------------------
在回调函数中操作它
的不同方法:
在这里,我有一个名为 Person 的构造函数。它有一个名为 name
的属性和四个名为 sayNameVersion1、sayNameVersion2、sayNameVersion3 和 sayNameVersion4
的方法。他们四个人都有一个特定的任务。接受回调并调用它。回调有一个特定的任务,即记录 Person 构造函数实例的 name 属性。
function Person(name){
this.name = name
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
this.sayNameVersion3 = function(callback){
callback.call(this)
}
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
}
function niceCallback(){
// Function to be used as callback
var parentObject = this
console.log(parentObject)
}
现在,让我们从 person 构造函数创建一个实例,并使用 niceCallback
调用不同版本的 sayNameVersionX
(X 指的是 1,2,3,4)方法,看看我们可以通过多少种方式来操作 this
内部回调来引用 person
实例。
var p1 = new Person('zami') // Create an instance of Person constructor
bind 的作用是创建一个新函数,并将 this
关键字设置为提供的值。
sayNameVersion1
并使用 bind 来操作回
调函数。sayNameVersion2
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
第一个通过方法本身内部的回调将其
绑定。对于第二个,回调是与绑定到它的对象一起传递的。
p1.sayNameVersion1(niceCallback) // pass simply the callback and bind happens inside the sayNameVersion1 method
p1.sayNameVersion2(niceCallback.bind(p1)) // uses bind before passing callback
调用方法的第一个
参数在附加了调用
的函数中用作此
参数。
sayNameVersion3
使用 call
来操作 this
来引用我们创建的 person 对象,而不是 window 对象。
this.sayNameVersion3 = function(callback){
callback.call(this)
}
它的称呼如下:
p1.sayNameVersion3(niceCallback)
与 call
类似,apply
的第一个参数是指将由 this
关键字指示的对象。
sayNameVersion4
使用 apply
来操作它
来引用 person 对象
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
它的称呼如下。只需传递回调,
p1.sayNameVersion4(niceCallback)
评论
我们不能将其绑定到 ,因为它总是与全局对象 (Window) 一起执行。如果你想在回调函数中访问上下文,那么通过使用回调函数,我们可以实现它:setTimeout()
this
bind()
setTimeout(function(){
this.methodName();
}.bind(this), 2000);
评论
Window
window
另一种方法是接口中的方法,这是自 DOM2 以来在事件侦听器中绑定的标准方法,它允许您始终删除侦听器(以及其他好处):this
handleEvent(evt)
EventListener
var obj = {
handleEvent(e) {
// always true
console.log(this === obj);
}
};
document.body.addEventListener('click', obj);
有关使用的详细信息可以在这里找到: DOM handleEvent:自 2000 年以来的跨平台标准handleEvent
评论
目前,如果在代码中使用类,还有另一种方法。
在类字段的支持下,可以通过以下方式实现:
class someView {
onSomeInputKeyUp = (event) => {
console.log(this); // This refers to the correct value
// ....
someInitMethod() {
//...
someInput.addEventListener('input', this.onSomeInputKeyUp)
可以肯定的是,在引擎盖下,绑定上下文的所有旧好箭头函数,但在这种形式下,它看起来比显式绑定更清晰。
由于这是一个第 3 阶段的提案,因此您现在(2018 年 8 月)需要 Babel 和适当的 Babel 插件来处理它。
评论
public methodName = (params) => { body }
你应该知道“这个”关键词。
根据我的观点,您可以通过三种方式实现“this”(Self|箭头功能|Bind 方法)
与其他语言相比,函数的关键字在 JavaScript 中的行为略有不同。this
它在严格模式和非严格模式之间也有一些区别。
在大多数情况下,此值由函数的调用方式决定。
在执行过程中无法通过赋值设置它,并且每次调用函数时都可能不同。
ES5 引入了 bind() 方法来设置函数的值,而不管它是如何调用的,this
ES2015 引入了箭头函数,这些函数不提供自己的绑定(它保留了封闭词法上下文的这个值)。this
方法1:Self - Self 用于维护对原始 this 的引用,即使上下文正在发生变化。这是事件处理程序中经常使用的一种技术(尤其是在闭包中)。
参考资料: this
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function () {
alert(self.data);
});
}
方法 2:箭头函数 - 箭头函数表达式在语法上是正则函数表达式的紧凑替代方法,尽管它没有与 this、arguments、super 或 new.target 关键字的绑定。
箭头函数表达式不适合用作方法,并且不能用作构造函数。
参考:箭头函数表达式
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',()=> {
alert(this.data);
});
}
方法 3:绑定 - bind() 方法创建一个新函数,该函数在调用时,其关键字设置为提供的值,并在调用新函数时提供的任何参数之前具有给定的参数序列。this
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',(function() {
alert(this.data);
}).bind(this);
问题围绕着关键字在 JavaScript 中的行为方式。 行为不同,如下所示,this
this
- 的值通常由函数执行上下文确定。
this
- 在全局作用域中,指全局对象(对象)。
this
window
- 如果为任何函数启用了严格模式,则 的值将与严格模式下一样,全局对象代替对象。
this
undefined
undefined
window
- 点前的对象是关键字将绑定到的对象。
this
- 我们可以用 、 和 显式设置 this 的值
call()
bind()
apply()
- 当使用关键字(构造函数)时,这将绑定到正在创建的新对象。
new
- 箭头函数不绑定,而是按词法绑定(即基于原始上下文)
this
this
正如大多数答案所建议的那样,我们可以使用箭头函数或 bind()
方法或 Self var。我会引用 Google JavaScript 风格指南中关于 lambdas(箭头函数)的观点
比起 f.bind(this),更喜欢使用箭头函数,尤其是在 goog.bind(f, 这个)。避免写 const self = this。箭头函数 对于回调特别有用,回调有时会意外传递 其他参数。
Google 明确推荐使用 lambda 而不是 bind 或 const self = this
因此,最好的解决方案是使用 lambda,如下所示:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
引用:
评论
this
在 JavaScript 中:
JavaScript 中的值 100% 取决于函数的调用方式,而不是函数的定义方式。我们可以通过“点法则的左边”相对容易地找到 的值:this
this
- 当使用 function 关键字创建函数时,其值是被调用的函数点左侧的对象
this
- 如果点上没有对象,那么函数内部的值通常是全局对象(在 Node.js 和浏览器中)。我不建议在这里使用关键字,因为它不如使用类似的东西那么明确!
this
global
window
this
window
- 存在某些构造,如箭头函数和使用 a 函数创建的函数,这些函数可以固定 的值。这些是规则的例外,但它们对于修复 的值非常有帮助。
Function.prototype.bind()
this
this
Node.js 中的示例
module.exports.data = 'module data';
// This outside a function in node refers to module.exports object
console.log(this);
const obj1 = {
data: "obj1 data",
met1: function () {
console.log(this.data);
},
met2: () => {
console.log(this.data);
},
};
const obj2 = {
data: "obj2 data",
test1: function () {
console.log(this.data);
},
test2: function () {
console.log(this.data);
}.bind(obj1),
test3: obj1.met1,
test4: obj1.met2,
};
obj2.test1();
obj2.test2();
obj2.test3();
obj2.test4();
obj1.met1.call(obj2);
输出:
让我一一引导您完成输出(忽略从第二个开始的第一个日志):
this
正是因为左边的点法则,我们可以看到是怎么叫的。 是点的左边,因此是值。obj2
test1
obj2.test1();
obj2
this
- 即使位于点的左边,也通过方法绑定。值为 。
obj2
test2
obj1
bind()
this
obj1
obj2
是调用的函数的点的左侧:。因此,将是 的值。obj2.test3()
obj2
this
- 在本例中:是点的左边。但是,箭头函数没有自己的绑定。因此,它将绑定到外部范围的值,该范围是一开始记录的对象。
obj2.test4()
obj2
this
this
module.exports
- 我们还可以使用函数指定 的值。在这里,我们可以将所需的值作为参数传入,在这种情况下。
this
call
this
obj2
我遇到了一个折线图函数的问题,该函数是从 HTML 调用的,如下所示: .Ngx
xAxisTickFormatting
[xAxisTickFormatting]="xFormat"
我无法从声明的函数访问组件的变量。这个解决方案帮助我解决了这个问题,找到了正确的方法。
而不是像这样使用函数:
xFormat (value): string {
return value.toString() + this.oneComponentVariable; //gives wrong result
}
使用这个:
xFormat = (value) => {
// console.log(this);
// now you have access to your component variables
return value + this.oneComponentVariable
}
其他一些人已经谈到了如何使用 .bind() 方法,但具体来说,如果有人无法让它们一起工作,您可以将其与 .then() 一起使用:
someFunction()
.then(function(response) {
//'this' wasn't accessible here before but now it is
}.bind(this))
如评论中所述,另一种方法是使用没有自己的“this”值的箭头函数
someFunction()
.then((response)=>{
//'this' was always accessible here
})
评论
this
this
.bind
这就是我解决问题的方式
class myClass
{
constructor(parent)
{
this.callback = (function() {
this.callbackFunctionOfParent();
}).bind(parent);
}
callCallback() {
this.callback();
}
}
class Class2
{
constructor()
{
this.Name = "CLASS 2";
this.test = new myClass(this);
this.test.callCallback();
}
callbackFunctionOfParent()
{
console.log("parent is: " + this.Name);
}
}
var c2 = new Class2;
评论
this.callback = function() { parent.callbackFunctionOfParent() }
function mul(x, y) { return x * y; } function add(x, y) { return mul(x, 1) + mul(y, 1); }; add(41, 1)
您可以使用箭头函数来避免此问题。
const functionToTest = (dataToSet , transport) => {
this.dataToSet = dataToSet ;
transport.on('dataToSet ', () => {
console.log(this.dataToSet);
});
}
评论