我有一个注册事件处理程序的构造函数:
函数 MyConstructor(数据,传输){ this.data = 数据; transport.on('data', function () { alert(this.data); }); } // 模拟传输对象 var transport = { on: function(event, callback) { setTimeout(callback, 1000); } }; // 调用 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
(又名“上下文”)是每个函数中的一个特殊关键字,其值仅取决于 如何函数被调用,而不是如何/何时/在何处定义它。它不像其他变量那样受词法范围的影响(箭头函数除外,见下文)。这里有些例子:
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 documentation。
如何参考正确的this
使用箭头函数
ECMAScript 6 引入了 箭头函数,可以将其视为 lambda 函数。它们没有自己的 this
绑定。相反,this
就像普通变量一样在范围内查找。这意味着您不必调用 .bind
。这不是他们唯一的特殊行为,请参阅 MDN 文档以获取更多信息。
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}
不要使用这个
您实际上并不想特别访问 this
,而是它引用的对象。这就是为什么一个简单的解决方案是简单地创建一个也引用该对象的新变量。变量可以有任何名称,但常见的是 self
和 that
。
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
由于 self
是一个普通变量,它遵循词法范围规则并且可以在回调中访问。这还有一个好处是您可以访问回调本身的 this
值。
显式设置回调 - 第 1 部分
看起来您无法控制 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]就是这样一种方法。它的签名是:
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
注意:您是否可以为 this
传递值通常会在该函数/方法的文档中提及。例如,jQuery's $.ajax
method [docs] 描述了一个名为 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();
以下是访问子上下文中的父上下文的几种方法 -
您可以使用 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);
如果您使用 Underscore.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);
});
}
这一切都在调用方法的“神奇”语法中:
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”关键字。
根据我的观点,您可以通过三种方式实现“this”(自/箭头函数/绑定方法)
与其他语言相比,函数的 this
关键字在 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 - bind() 方法创建一个新函数,在调用该函数时,将其 this
关键字设置为提供的值,并在新函数提供的任何参数之前设置给定的参数序列叫做。
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',(function() {
alert(this.data);
}).bind(this);
“上下文”的问题
术语“上下文”有时用于指代 this 引用的对象。它的使用是不恰当的,因为它在语义上或技术上都不适合 ECMAScript's this。
"Context" 是指围绕某事物增加意义的情况,或一些赋予额外意义的前后信息。术语“上下文”在 ECMAScript 中用于指代 execution context,即某些执行代码范围内的所有参数、范围和 this。
这显示在 ECMA-262 section 10.4.2 中:
将 ThisBinding 设置为与调用执行上下文的 ThisBinding 相同的值
这清楚地表明这是执行上下文的一部分。
执行上下文提供了为正在执行的代码增加意义的周围信息。它包含的信息远不止 thisBinding。
这不是“上下文”的价值。它只是执行上下文的一部分。它本质上是一个局部变量,可以通过调用任何对象并在严格模式下设置为任何值。
this
,但这里没有提供任何术语,而且现在关闭“上下文”的大门可能为时已晚。
首先,您需要清楚了解 scope
以及 this
关键字在 scope
上下文中的行为。
this
& scope
:
JavaScript 中有两种类型的作用域。他们是:
全局作用域函数作用域
简而言之,全局作用域是指窗口对象。在全局范围内声明的变量可以从任何地方访问。
另一方面,函数作用域位于函数内部。在函数内部声明的变量不能从外部正常访问。
全局范围内的 this
关键字指的是窗口对象。函数内部的 this
也指窗口对象。所以 this
将始终引用窗口,直到我们找到一种方法来操纵 this
以指示我们自己选择的上下文。
--------------------------------------------------------------------------------
- -
- 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 -
- -
--------------------------------------------------------------------------------
在回调函数中操作 this
的不同方法:
这里我有一个名为 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
和 sayNameVersion2
使用绑定来操作回调函数的 this
。
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
第一个将 this
与方法本身内部的回调绑定。而对于第二个,回调是与绑定到它的对象一起传递的。
p1.sayNameVersion1(niceCallback) // pass simply the callback and bind happens inside the sayNameVersion1 method
p1.sayNameVersion2(niceCallback.bind(p1)) // uses bind before passing callback
call
方法的 first argument
在使用 call
调用的函数内用作 this
附在上面。
sayNameVersion3
使用 call
来操纵 this
以引用我们创建的 person 对象,而不是 window 对象。
this.sayNameVersion3 = function(callback){
callback.call(this)
}
它被称为如下:
p1.sayNameVersion3(niceCallback)
与 call
类似,apply
的第一个参数是指将由 this
关键字指示的对象。
sayNameVersion4
使用 apply
操纵 this
来引用人员对象
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
它被称为如下。只需传递回调,
p1.sayNameVersion4(niceCallback)
我们无法将它绑定到 setTimeout()
,因为它总是使用 全局对象 (Window) 执行。如果你想在回调函数中访问 this
上下文,那么通过使用 bind()
到回调函数,我们可以实现它:
setTimeout(function(){
this.methodName();
}.bind(this), 2000);
Window
”:不是“window
”(小写)吗?
问题围绕着 this
关键字在 JavaScript 中的行为方式展开。 this
的行为如下所示,
this 的值通常由函数执行上下文确定。在全局范围内,this 指的是全局对象(window 对象)。如果为任何函数启用了严格模式,那么 this 的值将是未定义的,因为在严格模式下,全局对象引用未定义来代替窗口对象。位于点之前的对象是 this 关键字将绑定到的对象。我们可以使用 call()、bind() 和 apply() 显式设置 this 的值。当使用 new 关键字(构造函数)时, this 将绑定到正在创建的新对象。箭头函数不绑定 this - 相反,this 是按词法绑定的(即,基于原始上下文)
正如大多数答案所暗示的那样,我们可以使用 箭头函数或 bind()
Method 或 Self var。我会引用 Google JavaScript Style Guide 中关于 lambdas(箭头函数)的一点
更喜欢使用箭头函数而不是 f.bind(this),尤其是 goog.bind(f, this)。避免写 const self = this。箭头函数对回调特别有用,回调有时会传递意外的附加参数。
Google 明确建议使用 lambdas 而不是 bind 或 const self = this
所以最好的解决方案是使用如下的 lambdas,
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
参考:
https://medium.com/tech-tajawal/javascript-this-4-rules-7354abdb274c arrow-functions-vs-bind
如果在代码中使用类,目前还有另一种可能的方法。
在 class fields 的支持下,可以通过以下方式实现:
class someView {
onSomeInputKeyUp = (event) => {
console.log(this); // This refers to the correct value
// ....
someInitMethod() {
//...
someInput.addEventListener('input', this.onSomeInputKeyUp)
可以肯定的是,在底层,绑定上下文的都是旧的好的箭头函数,但在这种形式下,它看起来比显式绑定要清楚得多。
由于它是第 3 阶段提案,因此您需要 Babel 和适当的 Babel plugin 来处理它,就像现在 (08/2018) 一样。
public methodName = (params) => { body }
在一个类中。
我遇到了从 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
}
另一种方法是自 DOM2 以来在事件侦听器中绑定 this
的标准方法,让您始终删除侦听器(以及其他好处),是 { EventListener
接口中的 2} 方法:
var obj = {
handleEvent(e) {
// always true
console.log(this === obj);
}
};
document.body.addEventListener('click', obj);
可以在此处找到有关使用 handleEvent
的详细信息:DOM handleEvent: a cross-platform standard since year 2000
其他一些人已经谈到了如何使用 .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
值,而是使用来自 closes this
提供环境的值。 (2) 因此,.bind
对箭头函数没有影响。
这在 JavaScript 中:
JavaScript 中 this
的值 100% 取决于函数的调用方式,而不是函数的定义方式。我们可以通过'点规则的左边'相对容易地找到this
的值:
当使用 function 关键字创建函数时,this 的值是被调用函数的点左侧的对象如果点的左侧没有对象,那么函数内部 this 的值通常是全局对象(全局在 Node.js 和浏览器中的窗口中)。我不建议在这里使用 this 关键字,因为它比使用 window!存在某些构造,例如箭头函数和使用 Function.prototype.bind() 创建的函数,该函数可以修复 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);
输出:
https://i.stack.imgur.com/VvehZ.png
让我一个一个地引导你完成输出(忽略从第二个开始的第一个日志):
这是 obj2 因为点规则的左边,我们可以看到 test1 是如何被称为 obj2.test1(); 的。 obj2 在点的左侧,因此是 this 值。即使 obj2 在点的左侧, test2 通过 bind() 方法绑定到 obj1。这个值是obj1。 obj2 位于被调用函数的点的左侧:obj2.test3()。因此 obj2 将是 this 的值。在这种情况下: obj2.test4() obj2 在点的左边。但是,箭头函数没有自己的 this 绑定。因此,它将绑定到外部范围的 this 值,即模块。导出开始时记录的对象。我们也可以使用 call 函数来指定 this 的值。在这里,我们可以传入所需的 this 值作为参数,在本例中为 obj2。
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()
将在解释过程中第一次遇到它的上下文的快照...也就是说,当 JavaScript 到达bind()
函数 第一次,此时它将获取this
的上下文!由于无法更改bind()
的标准实现,因此排除故障可能会很棘手。一旦一个函数绑定到另一个对象,它将保持绑定到该对象,并且尝试重新绑定它将不起作用。