我有一个构造函数,它注册一个事件处理程序:
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`
要了解更多信息this
,请查看MDN文档.
this
this
实际上你并不想this
特别访问,而是它所引用的对象.这就是为什么一个简单的解决方案就是简单地创建一个也引用该对象的新变量.变量可以有任何名称,但常见的是self
和that
.
function MyConstructor(data, transport) { this.data = data; var self = this; transport.on('data', function() { alert(self.data); }); }
由于它self
是一个普通变量,它遵循词法范围规则,并且可以在回调中访问.这也有一个优点,您可以访问this
回调本身的值.
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
's 的值this
.
注意:绑定jQuery的上下文时,请改用jQuery.proxy
[docs].这样做的原因是,在解除对事件回调的绑定时,您不需要存储对该函数的引用.jQuery在内部处理.
ECMAScript 6引入了箭头函数,可以将其视为lambda函数.他们没有自己的this
约束力.相反,this
就像普通变量一样在范围内查找.这意味着你不必打电话.bind
.这不是他们唯一的特殊行为,请参阅MDN文档以获取更多信息.
function MyConstructor(data, transport) { this.data = data; transport.on('data', () => alert(this.data)); }
this
回调-第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的$.ajax
方法[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);
或者通过使用匿名函数作为回调/事件处理程序并将object(this
)分配给另一个变量,显式地将该函数作为对象的"方法"调用:
var self = this; document.body.onclick = function() { self.method(); };
或使用箭头功能:
document.body.onclick = () => this.method();
你可以使用bind()
功能.
将context/this的引用存储在另一个变量中(参见下面的示例).
使用ES6 箭头功能.
改变代码/功能设计/架构 - 为此你应该在javascript中控制 设计模式.
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));
function MyConstructor(data, transport) {
var self = this;
this.data = data;
transport.on('data', function() {
alert(self.data);
});
}
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
术语"上下文"有时用来指通过引用的对象此.它的使用是不合适的,因为它不适合任何语义或技术上与ECMAScript中的这个.
"上下文"是指围绕某些事物增加意义的情况,或者是一些提供额外含义的前后信息.ECMAScript中使用术语"上下文"来指代执行上下文,它是一些执行代码范围内的所有参数,范围和这一点.
这在ECMA-262第10.4.2节中显示:
将ThisBinding设置为与调用执行上下文的ThisBinding相同的值
这清楚地表明这是执行上下文的一部分.
执行上下文提供周围信息,为正在执行的代码添加含义.它包含了更多只有thisBinding的信息.
所以价值这不是"背景",它只是一个执行上下文的一部分.它本质上是一个局部变量,可以通过调用任何对象并在严格模式下设置为任何值.
这都是调用方法的"神奇"语法:
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));
当前,如果在代码中使用类,则可能存在另一种方法。
在类字段 的支持下,可以进行其他操作:
class someView { onSomeInputKeyUp = (event) => { console.log(this); // this refers to correct value // .... someInitMethod() { //... someInput.addEventListener('input', this.onSomeInputKeyUp)
可以肯定的是,绑定上下文的都是老式的好箭头函数,但是以这种形式,它看起来比显式绑定更清晰。
由于是Stage 3 Proposal,因此您现在需要Babel和适当的Babel插件来处理它(08/2018)。
这个问题围绕着this
关键字在javascript中的行为方式展开。this
表现如下
的值this
通常由函数执行上下文确定。
在全局范围内,this
指的是全局对象(window
object)。
如果为任何功能启用了严格模式,则的值this
将undefined
与严格模式中的一样,全局对象将undefined
代替window
对象。
此关键字将绑定到点之前的对象。
我们可以明确地设置这个值call()
,bind()
和apply()
当使用new
关键字(构造函数)时,它将绑定到正在创建的新对象。
箭头函数不绑定this
?,而是按this
词法绑定(即基于原始上下文)
正如大多数答案所暗示的,我们可以使用箭头函数或bind()
方法或自变量。我会从Google JavaScript样式指南中引用有关lambdas(箭头功能)的观点
相对于f.bind(this),尤其是goog.bind(f,this),首选使用箭头函数。避免编写const self = this。箭头函数对于回调有时特别有用,该回调有时会传递意外的其他参数。
Google明确建议使用Lambda而不是bind或 const self = this
因此最好的解决方案是使用如下所示的lambda,
function MyConstructor(data, transport) { this.data = data; transport.on('data', () => { alert(this.data); }); }
参考文献:
https://medium.com/tech-tajawal/javascript-this-4-rules-7354abdb274c
箭头功能与绑定
首先,您需要在上下文中清楚地了解关键字scope
和行为.this
scope
this
&scope
:
there are two types of scope in javascript. They are : 1) Global Scope 2) Function Scope
简而言之,全局范围是指窗口对象.在全局范围内声明的变量可以从任何地方访问.另一方面,函数范围驻留在函数内部.函数内部声明的变量通常无法从外部访问.this
全局范围中的关键字是指窗口对象.this
inside函数也指window对象.所以this
我们总是会引用窗口,直到我们找到一种操作方式this
来指示我们自己选择的上下文.
-------------------------------------------------------------------------------- - - - Global Scope - - ( globally "this" refers to window object) - - - - function outer_function(callback){ - - - - // outer function scope - - // inside outer function"this" keyword refers to window object - - - callback() // "this" inside callback also refers window object - - } - - - - function callback_function(){ - - - - // function to be passed as callback - - - - // here "THIS" refers to 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构造函数创建一个实例,并调用不同版本的sayNameVersionX
(X指向1,2,3,4)方法,niceCallback
以查看我们可以通过多少方式操作this
内部回调来引用该person
实例.
var p1 = new Person('zami') // create an instance of Person constructor
绑定:
绑定的作用是创建一个新的函数,并将this
关键字设置为提供的值.
sayNameVersion1
并sayNameVersion2
使用bind来操纵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
电话:
在first argument
所述的call
方法被用作this
该被调用,该函数内call
连接到它.
sayNameVersion3
使用 call
操纵this
参考,而不是窗口对象,我们创建的Person对象.
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);
你应该知道"这个"关键字.
根据我的观点,你可以用三种方式实现"this" (自我/箭头功能/绑定方法)
与其他语言相比,函数的此关键字在JavaScript中的行为略有不同.
它在严格模式和非严格模式之间也有一些区别.
在大多数情况下,其值取决于函数的调用方式.
它不能在执行期间通过赋值设置,并且每次调用函数时都可能不同.
ES5引入了bind()方法来设置函数的值,无论它如何被调用,
和ES2015引入了箭头函数,这些函数不提供它们自己的这种绑定(它保留了封闭词汇上下文的这个值).
方法1:即使在上下文发生变化时,也会使用自我来维护对原始引用的引用.这是一种常用于事件处理程序的技术(特别是在闭包中).
参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
function MyConstructor(data, transport) { this.data = data; var self = this; transport.on('data', function () { alert(self.data); }); }
方法2:箭头函数 - 箭头函数表达式是常规函数表达式的语法紧凑替代方法,
虽然没有自己绑定到this,arguments,super或new.target关键字.
箭头函数表达式不适合作为方法,它们不能用作构造函数.
参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
function MyConstructor(data, transport) { this.data = data; transport.on('data',()=> { alert(this.data); }); }
方法3:绑定 - bind()方法创建一个新函数,
调用时,将其关键字设置为提供的值,
在调用新函数时,在任何提供的参数序列之前使用给定的参数序列.
参考: https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind
function MyConstructor(data, transport) { this.data = data; transport.on('data',(function() { alert(this.data); }).bind(this);