我有一个构造函数注册一个事件处理程序:
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
使用箭头功能
ECMAScript 6引入了箭头函数,可以将其视为lambda函数。他们没有自己的this
约束力。而是this
像普通变量一样在范围内查找。这意味着您不必打电话.bind
。这不是它们唯一的特殊行为,请参考MDN文档以获取更多信息。
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
回调本身的值。
显式设置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在内部进行处理。
设置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的[docs]$.ajax
方法描述了一个名为的选项context
:
该对象将成为所有与Ajax相关的回调的上下文。
常见问题:使用对象方法作为回调/事件处理程序
此问题的另一个常见表现是将对象方法用作回调/事件处理程序。函数是JavaScript中的一等公民,术语“方法”仅是一个俗称的函数,即对象属性的值。但是该函数没有指向其“包含”对象的特定链接。
考虑以下示例:
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = function() {
console.log(this.data);
};
该函数this.method
被分配为click事件处理程序,但是如果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()
功能。 - 将对上下文/ 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将对上下文/ 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引用的对象。它的使用是不合适的,因为它在语义上或技术上都不适合ECMAScript的this。
“上下文”是指围绕某些事物增加含义的环境,或一些提供额外含义的前后信息。术语“上下文” ECMAScript中用于指代执行上下文,这是所有的参数,范围和这个的一些执行的代码的范围内。
将ThisBinding设置为与调用执行上下文的ThisBinding相同的值
这清楚地表明这是执行上下文的一部分。
执行上下文提供了周围的信息,这些信息为正在执行的代码增加了含义。它不仅包含thisBinding,还包含更多信息。
因此,此值不是“上下文”,而只是执行上下文的一部分。它本质上是一个局部变量,可以通过对任何对象的调用以及在严格模式下将其设置为所有值。
您应该了解“ this”关键字。
根据我的观点,您可以通过三种方式
(自我/箭头功能/绑定方法)实现“本”
与其他语言相比,函数的this关键字在JavaScript中的行为略有不同。
在严格模式和非严格模式之间也有一些区别。
在大多数情况下,其值取决于函数的调用方式。
在执行过程中不能通过赋值来设置它,并且每次调用该函数时可能会有所不同。
ES5引入了bind()方法来设置函数this的值,而不管其调用方式如何,
并且ES2015引入了箭头函数,它们不提供自己的绑定(它保留了封闭的词法上下文的此值)。
方法1:自我-自我用于保持对原始内容的引用,即使上下文在变化。这是事件处理程序中经常使用的一种技术(尤其是在闭包中)。
参考:https : //developer.mozilla.org/zh-CN/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/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',()=> {
alert(this.data);
});
}
方法3:绑定-bind()方法创建一个新函数,
调用时,将其this关键字设置为提供的值,
在调用新函数时提供的给定参数序列之前。
参考: https : //developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_objects/Function/bind
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',(function() {
alert(this.data);
}).bind(this);
首先,您需要对的上下文中scope
的this
关键字有清晰的了解和行为scope
。
this
&scope
:
there are two types of scope in javascript. They are :
1) Global Scope
2) Function Scope
简而言之,全局作用域是指window对象。在全局作用域中声明的变量可以从任何地方访问;另一方面,函数作用域位于函数内部。在函数内部声明的变量通常不能从外部访问。this
全局范围内的关键字是指window对象。this
内部函数还指代窗口对象,因此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
来引用我们创建的人员对象,而不是窗口对象。
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中的行为方式展开。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);
});
}
参考文献:
当前,如果在代码中使用类,则还有另一种方法。
在类字段 的支持下,可以进行以下操作:
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)。
另一种方法,这是因为DOM2标准的方式绑定this
事件监听器内,这让你随时删除监听器(还有其他好处),是handleEvent(evt)
从方法EventListener
接口:
var obj = {
handleEvent(e) {
// always true
console.log(this === obj);
}
};
document.body.addEventListener('click', obj);
有关使用的详细信息,handleEvent
可以在这里找到:https : //medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38
this
在JS中:
this
JS中的值100%由函数的调用方式决定,而不是由如何定义的方式决定。我们可以this
通过“点规则的左侧”相对容易地找到的值:
- 使用function关键字创建函数时,的值
this
是被调用函数的点的左侧的对象 - 如果点上没有剩余的对象,则
this
函数内部的值通常是全局对象(global
在节点中,window
在浏览器中)。我不建议在this
此使用关键字,因为它比使用window
!之类的关键字更不明确。 - 存在某些构造,例如箭头函数和使用
Function.prototype.bind()
a函数创建的函数,可以固定的值this
。这些是规则的例外,但对于确定的值确实很有帮助this
。
nodeJS中的示例
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
在这种情况下就是这样。
文章标签:callback , javascript , this
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!
评论已关闭!