為什麼細微的語法的變化就可以改變this的值?

掃書時看到這句話。背景:

var name = "The Window";

var object = {
name : "My Object",
getName : function(){
return this.name;
}
}

object.getName();
(object.getName)();// "My Object"
(object.getName = object.getName)();// "The Window"

第三句先執行了一句賦值語句,為何this的值就發生了改變?


瀉藥

大白話就是

func 在沒call apply bind 的情況下

this 是指 函數調用時候 "點" (「[]」中括弧)前頭那個對象

而函數直接調用時候 (前頭沒點和[])

this 就是全局對象

(object.getName = object.getName)();

賦值語句執行結果是拿到 getName函數

這個函數()執行下

前頭沒點和[]

那 this 就是全局對象

==== 補充 ====

評論里有人問第二個(object.getName)() 解釋不通

話說能看看規範,就別瞎猜了

分組運算符不進行求值(GetValue)

第三個求值是賦值運算符乾的

分組跟他這裡的this改變沒啥關係(不影響)


這個我記得微博上有過這個問題,當時貘大@貘吃饃香已經給了正確的原因...

以下是正經回答:

第三行 『(object.getName = object.getName) 』圓括弧里的內容是AssignmentExpression,它的返回值是『GetValue(等號右邊的表達式)』

注意這個GetValue是規範中的抽象操作,object.getName經過這個GetValue操作之後就不是Reference type了,丟失了它的this信息。

而第二行實際和第一行是一樣的。這個圓括弧在規範中叫grouping operator,它不執行GetValue操作,所以函數調用的時候函數知道自己是通過object進行調用的。
(實際上你可以試一下,『(object.foo) = 1)』是合法的語句)

規範中的相應部分:
[1] 關於函數調用的部分,在這裡你可以看出來function call在不同情況下對this的處理
http://www.ecma-international.org/ecma-262/7.0/index.html#sec-function-calls-runtime-semantics-evaluation

[2] AssignmentExpression的執行過程 http://www.ecma-international.org/ecma-262/7.0/index.html#sec-assignment-operators-runtime-semantics-evaluation

[3] Grouping operator的執行過程 http://www.ecma-international.org/ecma-262/7.0/index.html#sec-grouping-operator-runtime-semantics-evaluation


JavaScript This 的六道坎


誰調用,this 就指向誰。

在 Javascript 中,this的指向一句話就可以說明,不知道為什麼到了部分人的手裡就成了痛點。

在 JS 中,所有的調用都可以看做屬性的調用,函數也是一樣,如果沒有直接調用者,那麼 this 就指向 window。

var name = "The Window";

var object = {
name : "My Object",
getName : function(){
return this.name;
}
}

object.getName();
(object.getName)();// "My Object"
(object.getName = object.getName)();// "The Window"

以上述例子為例,

* object.getName: 調用者為 object

* (object.getName):調用者為 object

* object.getName = object.getName, 賦值語句更改了引用,調用者不在是object,但有沒有指定的調用者,所以 this 指向window.

所以無論怎麼變,最終只要關注調用者是誰即可。


js中的this確實很麻煩,不過我總結了兩條簡單的規則:

this屬於哪個函數

該函數是如何被調用的


在沒有var、const、let的情況下執行賦值語句,會返回一個值,就是賦值的值。

在第三句執行賦值語句後,直接返回getName。再調用就是在window下調用。

就跟以下語句等效:

var fn = object.getName; fn() // "The Window"


瀉藥。

關於 this 的指向,記住誰調用函數,它就指向誰。

看上去第三個調用和第二個沒有區別。其實有區別。

object.getName 是一個方法,賦值給 object.getName後,object.getName就是一個函數了,然後在掉用它,this的指向就是 Window了。

把第三句執行的 object.getName 改一下,就不具備那麼迷惑性了

var name = "Window Object";
var object = {
name: "My object",
getName: function getName() {
return this.name;
}
}
var h=object.getName;
h(); //"Window Object"
h; //getName(),h經過賦值,成為一個函數
(p=object.getName)(); //"Window Object"
p; //getName(),p是函數


js的this飄來飄去是日常,強行改變this指向也是日常,習慣了就好。


對於這個第三個賦值語句,本菜雞試著用大白話來回答一下吧:

(object.getName = object.getName)();

對於等號右邊的object.getNamejs會對他進行RHS查詢,就是引擎告訴編譯器試著找object.getName的值,編譯器在object那個作用域找到了,對引擎說:看 !!值是 function(){return this.name}

然後等式的返回值是等式右邊的值於是object.getName = object.getName 返回值就是function (){return this.name} 所以等式就變成了(function (){return this.name})()

引擎接著執行這段,然後告訴編譯器,幫我找找 this.name 是多少?

編譯器這時候會在當前作用域,(全局)下找name ,(試著想在空白js文件中寫入(function(){return this.name})())發現有了,告訴引擎,找到了,是the window ,這時候語句執行完了,返回the window


因為賦值表達式的結果是這個函數。這個函數是返回this.name啊,這this是全局


推薦閱讀:

在 103 個數中有 50 個數出現了兩次、3 個數出現了一次,如何找出出現了一次的 3 個數?
產品經理面試題——人人網的?
項目經理面試問題解析?
HR 臨時取消面試的真正原因是什麼?
我準備做銷售,面試需要準備什麼,最厲害的面試技巧是怎樣的?

TAG:JavaScript | 前端入門 | 面試問題 | 閉包 |