我注意到,在Stack Overflow网站上的JavaScript中,似乎没有明确解释this
关键字是什么以及如何正确使用(正确)。
我目睹了它的一些非常奇怪的行为,并且未能理解为什么会发生它。this
如何工作,何时应使用?
最佳答案
我建议先阅读Mike West的文章Scope in JavaScript(mirror)。这是对JavaScript中this
和作用域链的概念的出色而友好的介绍。
一旦开始习惯this
,规则实际上非常简单。 ECMAScript 5.1 Standard定义this
:
§11.1.1 this
关键字
this
关键字的值等于当前执行上下文的ThisBinding的值
这个绑定是JavaScript解释器在评估JavaScript代码时所维护的,例如特殊的CPU寄存器,其中包含对对象的引用。每当在以下三种情况之一中建立执行上下文时,解释器都会更新ThisBinding:
1.初始全局执行上下文
在顶层评估的JavaScript代码就是这种情况,例如直接在<script>
内部时:
<script>
alert("I'm evaluated in the initial global execution context!");
setTimeout(function () {
alert("I'm NOT evaluated in the initial global execution context.");
}, 1);
</script>
在初始全局执行上下文中评估代码时,ThisBinding设置为全局对象
window
(§10.4.1.1)。输入评估码
…直接致电
eval()
ThisBinding保持不变;它与调用执行上下文的ThisBinding(§10.4.2(2)(a))的值相同。
…如果不是直接致电
eval()
将这个Binding设置为全局对象,就好像在初始全局执行上下文(§10.4.2(1))中执行一样。
§15.1.2.1.1定义了直接调用
eval()
的含义。基本上,eval(...)
是直接调用,而类似(0, eval)(...)
或var indirectEval = eval; indirectEval(...);
的东西是对eval()
的间接调用。有关何时可以使用间接eval()
调用的信息,请参见chuckj's answer至(1, eval)('this') vs eval('this') in JavaScript?和Dmitry Soshnikov’s ECMA-262-5 in detail. Chapter 2. Strict Mode.。输入功能码
调用函数时会发生这种情况。如果在对象上(例如在
obj.myMethod()
或等效的obj["myMethod"]()
中)调用了函数,则ThisBinding设置为对象(示例中为obj
; §13.2.1)。在大多数其他情况下,ThisBinding设置为全局对象(§10.4.3)。之所以写“在大多数情况下”,是因为有八个ECMAScript 5内置函数可以在参数列表中指定ThisBinding。这些特殊功能采用了所谓的
thisArg
,当调用函数(§10.4.3)时,它成为ThisBinding。这些特殊的内置函数是:
Function.prototype.apply( thisArg, argArray )
Function.prototype.call( thisArg [ , arg1 [ , arg2, ... ] ] )
Function.prototype.bind( thisArg [ , arg1 [ , arg2, ... ] ] )
Array.prototype.every( callbackfn [ , thisArg ] )
Array.prototype.some( callbackfn [ , thisArg ] )
Array.prototype.forEach( callbackfn [ , thisArg ] )
Array.prototype.map( callbackfn [ , thisArg ] )
Array.prototype.filter( callbackfn [ , thisArg ] )
对于
Function.prototype
函数,它们是在函数对象上调用的,而不是将ThisBinding设置为函数对象,而是将ThisBinding设置为thisArg
。对于
Array.prototype
函数,在执行上下文中调用给定的callbackfn
,其中,如果提供,则此绑定设置为thisArg
;否则,转到全局对象。这些是纯JavaScript的规则。当您开始使用JavaScript库(例如jQuery)时,您可能会发现某些库函数会操纵
this
的值。这些JavaScript库的开发人员这样做是因为它倾向于支持最常见的用例,并且该库的用户通常会发现此行为更加方便。将引用this
的回调函数传递给库函数时,应确保在调用该函数时有关this
的值的任何保证,请参考文档。如果您想知道JavaScript库如何处理
this
的值,则该库仅使用接受thisArg
的内置JavaScript函数之一。您也可以使用回调函数和thisArg
编写自己的函数:function doWork(callbackfn, thisArg) {
//...
if (callbackfn != null) callbackfn.call(thisArg);
}
我还没有提到一种特殊情况。通过
new
运算符构造新对象时,JavaScript解释器会创建一个新的空对象,设置一些内部属性,然后在新对象上调用构造函数。因此,在构造函数上下文中调用函数时,this
的值是解释器创建的新对象:function MyType() {
this.someData = "a string";
}
var instance = new MyType();
// Kind of like the following, but there are more steps involved:
// var instance = {};
// MyType.call(instance);
箭头功能
Arrow functions(在ECMA6中引入)更改了
this
的范围。有关更多信息,请参见现有的规范问题Arrow function vs function declaration / expressions: Are they equivalent / exchangeable?。简而言之:箭头函数没有自己的
this
....绑定。相反,这些标识符像任何其他方法一样在词法范围内解析
其他变量。这意味着在箭头函数中,
this
...指的是环境中this
的值定义了箭头功能。
只是为了好玩,用一些例子测试您的理解
要显示答案,请将鼠标悬停在浅黄色框上。
标记的行中
this
的值是什么?为什么?window
—在初始全局执行上下文中评估标记的行。if (true) {
// What is `this` here?
}
当执行
this
时,标记行上的obj.staticFunction()
值是什么?为什么?obj
—在对象上调用函数时,ThisBinding设置为该对象。var obj = {
someData: "a string"
};
function myFun() {
return this // What is `this` here?
}
obj.staticFunction = myFun;
console.log("this is window:", obj.staticFunction() == window);
console.log("this is obj:", obj.staticFunction() == obj);
标记的行中
this
的值是什么?为什么?window
在此示例中,JavaScript解释器输入函数代码,但是由于未在对象上调用
myFun
/ obj.myMethod
,因此ThisBinding设置为window
。这与Python不同,在Python中,访问方法(
obj.myMethod
)创建一个bound method object。var obj = {
myMethod: function () {
return this; // What is `this` here?
}
};
var myFun = obj.myMethod;
console.log("this is window:", myFun() == window);
console.log("this is obj:", myFun() == obj);
标记的行中
this
的值是什么?为什么?window
这个很棘手。评估评估代码时,
this
为obj
。但是,在评估代码中,未在对象上调用myFun
,因此对于该调用,ThisBinding设置为window
。function myFun() {
return this; // What is `this` here?
}
var obj = {
myMethod: function () {
eval("myFun()");
}
};
标记的行中
this
的值是什么?为什么?obj
myFun.call(obj);
行正在调用特殊的内置函数Function.prototype.call()
,该函数接受thisArg
作为第一个参数。function myFun() {
return this; // What is `this` here?
}
var obj = {
someData: "a string"
};
console.log("this is window:", myFun.call(obj) == window);
console.log("this is obj:", myFun.call(obj) == obj);