“ this”关键字如何工作?
[§11.1.1]的this关键字 所述this关键字的计算结果为当前执行上下文的ThisBinding的值
this
所述this关键字的计算结果为当前执行上下文的ThisBinding的值
这个绑定是JavaScript解释器在评估JavaScript代码时所维护的,例如特殊的CPU寄存器,其中包含对对象的引用。每当在以下三种情况之一中建立执行上下文时,解释器都会更新ThisBinding:
在顶级代码中评估的JavaScript代码就是这种情况,例如,直接在内时<script>:
<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。
window
…通过直接调用eval() ThisBinding保持不变;它与调用执行上下文的[ThisBinding](第[10.4.2](2)(a)节)的值相同。
eval()
…如果不是通过直接调用eval() ThisBinding ,则将其设置为全局对象 ,就像 在初始全局执行上下文中执行一样(第10.4.2(1)节)。
§15.1.2.1.1定义了直接调用eval()是什么。基本上eval(...)是直接调用,而(0, eval)(...)或var indirectEval = eval; indirectEval(...);则是的间接调用eval()。
eval(...)
(0, eval)(...)
var indirectEval = eval; indirectEval(...);
调用函数时会发生这种情况。如果在某个对象(例如in obj.myMethod()或等效对象)中调用了函数obj["myMethod"](),则ThisBinding设置为该对象(obj在示例中;第[13.2.1节])。在大多数其他情况下,ThisBinding设置为全局对象(第[10.4.3节])。
obj.myMethod()
obj["myMethod"]()
obj
之所以写“在大多数情况下”,是因为有八个ECMAScript 5内置函数可以在参数列表中指定ThisBinding。这些特殊函数采用一个所谓thisArg的形式,当调用该函数时(第[10.4.3节]),它成为[ThisBinding]。
thisArg
这些特殊的内置函数是:
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。
Function.prototype
对于Array.prototype函数,callbackfn在执行上下文中调用给定,thisArg如果提供,则将ThisBinding设置为;否则,转到全局对象。
Array.prototype
callbackfn
这些是纯JavaScript的规则。当您开始使用JavaScript库(例如jQuery)时,您可能会发现某些库函数会操纵的值this。这些JavaScript库的开发人员这样做是因为它倾向于支持最常见的用例,并且该库的用户通常会发现此行为更加方便。当传递引用this库函数的回调函数时,您应参考文档以获取有关this调用该函数时值的任何保证。
如果您想知道JavaScript库如何处理的值this,则该库只是使用接受的内置JavaScript函数之一thisArg。您也可以使用回调函数和编写自己的函数thisArg:
function doWork(callbackfn, thisArg) { //... if (callbackfn != null) callbackfn.call(thisArg); }
我还没有提到一个特殊情况。通过new运算符构造新对象时,JavaScript解释器会创建一个新的空对象,设置一些内部属性,然后在新对象上调用构造函数。因此,在构造函数上下文中调用函数时,的值this是解释器创建的新对象:
new
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 functions与函数声明/表达式:它们是否等效/可互换?]想要查询更多的信息。简而言之:
Arrow functions没有自己的this....绑定。相反,这些标识符像任何其他变量一样在词法范围内解析。这意味着在Arrow functions数中,this…指的是this在其中定义Arrow functions的环境中的值。
要显示答案,请将鼠标悬停在浅黄色框上。
window —在初始全局执行上下文中评估标记的行。
if (true) { // What is `this` here? }
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);
window 在此示例中,JavaScript解释器输入功能代码,但是由于未在对象上调用myFun/ obj.myMethod,因此ThisBinding设置为window。 这与Python不同,在Python中,访问method(obj.myMethod)会创建一个绑定的方法对象。
在此示例中,JavaScript解释器输入功能代码,但是由于未在对象上调用myFun/ obj.myMethod,因此ThisBinding设置为window。
myFun
obj.myMethod
这与Python不同,在Python中,访问method(obj.myMethod)会创建一个绑定的方法对象。
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);
window 这个很棘手。评估评估码时,this为obj。但是,在eval代码中,myFun未在对象上调用,因此将ThisBinding设置为window进行调用。
这个很棘手。评估评估码时,this为obj。但是,在eval代码中,myFun未在对象上调用,因此将ThisBinding设置为window进行调用。
function myFun() { return this; // What is `this` here? } var obj = { myMethod: function () { eval("myFun()"); } };
obj 该行myFun.call(obj);正在调用特殊的内置函数Function.prototype.call(),该函数接受thisArg作为第一个参数。
该行myFun.call(obj);正在调用特殊的内置函数Function.prototype.call(),该函数接受thisArg作为第一个参数。
myFun.call(obj);
Function.prototype.call()
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);