2017-07-11 59 views
3

我目前正在學習JavaScript,通過下面的書「你不知道JS」系列。什麼是允許`p.foo = o.foo`返回對函數`foo`的引用的JavaScript機制/規則?

在「此&對象原型」節中,討論「到功能間接引用」,作者狀態

function foo() { 
    console.log(this.a); 
} 

var a = 2; 
var o = { a: 3, foo: foo }; 
var p = { a: 4 }; 

o.foo(); // 3 
(p.foo = o.foo)(); // 2 

賦值表達式p.foo的結果值時= o.foo是一個 引用只是基礎功能對象。因此, 有效的呼叫站點只是foo(),而不是p.foo()或o.foo(),因爲您可能需要 。根據上述規則,默認綁定規則適用。

顯然,(p.foo = o.foo)返回對函數foo的引用。但是允許(p.foo = o.foo)返回對函數foo的引用的機制/規則是什麼?換句話說,爲什麼簡單的賦值返回對foo函數的引用?

+1

因爲賦值評估爲右側的*值*。而不是屬性引用,因爲方法調用'o.foo()'中的'o.foo'會做。 – Bergi

回答

2

當我想了解這樣的事情時,我發現將它逐步分解是有幫助的。

  1. o.foo着眼於o對象,並發現了一個名爲foo屬性。它返回一個引用屬性,不管它可能是什麼。在這種情況下,o.foo屬性是對函數foo的引用。
  2. p.foo = o.foo取自上面的結果(對函數foo的引用),在p對象中創建一個屬性,該對象也被命名爲foo。所以現在p.foo也是foo函數的參考,與o.foo完全一樣。
  3. 該表達式包含在圓括號中,因此現在您可以在=符號的左側或p.foo的左側找到它,這是(作爲提醒)仍然是對foo函數的引用。
  4. 現在我們在最後找到()。這會調用我們現有的功能。這是foo函數。特別請注意,我們是而不是調用p.foo()。這將是一個方法調用的功能,p.foo是一個參考,所以在該功能中,this將被設置爲p。但我們沒有這樣做。我們只是調用(p.foo = o.foo)返回的任何函數。與以前一樣,這與foo函數的功能相同,但我們現在已經失去了與o對象或p對象之間的任何連接。
  5. 因此,當我們在最後打電話時,我們只是調用foo函數,而不將this設置爲任何特定對象。因此,當我們撥打電話時,this設置爲undefined
  6. 但我們不是在strict模式下運行,所以JavaScript的「有益」不希望給我們一個未定義this,因此它設置this在瀏覽器中的window對象或節點的global對象。我們之前做過var a = 2;。因此windowglobal對象實際上現在具有名爲a的屬性,並且該屬性的值爲2
  7. 因此,現在當我們做console.log(this.a)時,我們從windowglobal對象中選取a屬性。該值爲2

如果所有這些代碼沒有在全局級運行,而是在一個函數內呢?那麼會發生什麼?

function test() { 
 
    function foo() { 
 
    console.log(this.a); 
 
    } 
 
    
 
    var a = 2; 
 
    var o = { a: 3, foo: foo }; 
 
    var p = { a: 4 }; 
 

 
    o.foo(); // 3 
 
    (p.foo = o.foo)(); // was 2, but now is undefined 
 
} 
 

 
test();

現在,當我們調用console.log(this.a);foothis還是指windowglobal對象。但是,當我們設置var a = 2;時,我們不再設置全球財產。我們只是創建一個局部變量。 window.aglobal.aundefined(除非其他代碼先前設置了它)。

嚴格模式避免了一些這種古怪。如果我們將'use strict';置於代碼的頂部,它將以嚴格模式編譯。現在,當我們在最後調用foo函數(又不是方法!)的最後一個函數調用時,現在將this設置爲undefined而不是windowglobal。因此,當我們嘗試撥打console.log(this.a)時,代碼失敗,因爲thisundefined相同,而undefined不具有(也不可能)具有a屬性。

讓我們試一下:

'use strict'; 
 

 
function foo() { 
 
    console.log(this.a); 
 
} 
 

 
var a = 2; 
 
var o = { a: 3, foo: foo }; 
 
var p = { a: 4 }; 
 

 
o.foo(); // 3 
 
(p.foo = o.foo)(); // was 2 in the original, but now throws an exception

底線,至少在這個特殊的例子:總是用嚴格的方式!這是你的朋友。

+1

我剛剛發佈了一個不完整的答案,基本上來自你的第8步。感謝您理解我無法解決的問題。我知道這是'窗口',但不知道爲什麼。 Upvoted :-) –

+2

實際上,在第5步中,'this'具體作爲'undefined'傳遞。第8步是無關緊要的 - 它與這種呼叫無關。這是第6步,馬虎模式函數將'this'值轉換爲對象,並用全局對象替換'null'或'undefined'。 – Bergi

+0

謝謝!這就是我所做的「意識流」概覽。 :-)我用你的更正更新了它,希望這次更接近它! (對於閱讀評論的人,我拿出第8步,所以9是新的8 ...) –

1

賦值運算符=是JavaScript中的一個表達式,它產生(返回)指定的值。因爲它是一個表達式,所以可以在任何允許使用表達式的地方使用,例如在括號內。

例如:

let test = (a = b = c = { name: 'test' }) 

上面的代碼將首先評估在括號中的表達和指向變量cb,並a到測試對象(以該順序),那麼它會指向test從這個表達式產生的價值。執行該行後,a,b,ctest都將指向同一個對象。

同樣,

(p.foo = o.foo) 

會產生o.foo回(在技術上它會產生什麼o.foo指向,這是功能foo)。

至於

(p.foo = o.foo)() 

通過添加parenths後附加(),我們告訴我們要調用任何表達(p.foo = o.foo)最終生產發動機。因此我們最終調用foo功能。 Similar patterns is used in IIFEs

一個有用的改寫將是思而行的上面這樣做:

let produced = (p.foo = o.foo) 
produced() 

Further reading about statements vs expressions

+0

這並不能解釋爲什麼'(p.foo = o.foo)()'''log's 2' –

+0

@FredGandt但這不是問題?問:「但是允許(p.foo = o.foo)返回對函數foo的引用的機制/規則是什麼?「 – nem035

+0

公平點(審查);也許它應該是; - ) –

相關問題