關於 JavaScript 常見的 Multiple left-hand assignment 考題
前言
Multiple left-hand assignment(連續左側賦值) 可以說是 JavaScript 常見考題,而這又稱之為連續賦值(Chain Variable Assignments),例如:a = b = c 這種讓人匪夷所思的題目,所以就寫一篇關於這一個到底是怎麼運作的。
連續左側賦值(Multiple left-hand assignment)
「Multiple left-hand assignment」中文是連續左側賦值,相信你應該會看過一些考題是這種
1 | var a = 1; |
基本上這個題目算是很簡單簡單,但是本身涵蓋的觀念就不少。
舉例來講等號運算子的相依性是以右至左,因此 c
會優先被賦予到 b
,而這賦予的過程是一個表達式,因此 a
就會吃下 b = c
表達式的結果也就是 3
。
拆開來看的話,就會變成以下
1 | var a = 1; |
或者你也可以看成以下
1 | var a = 1; |
但是 JavaScript 是一個很美麗(機車)的語言,類似的題目只需要稍微調整一下就可以變成另一個全新考題
1 | var a = b = c = 1; |
因此一個「Multiple left-hand assignment」概念是可以在 JavaScript 中驗證相當多觀念,拿剛剛的 var a = b = c = 1;
來講,就可以看成是這樣
1 | var var1 = (var2 = (var3 = 1)); |
雖然結果都會是 1
,但是如果再稍微調整一下題目你會發現一個奇妙的狀況
1 | console.log(a); // undefined |
那麼這個問題是出在哪裡了?主要是出在 var
語法的提升(Hoisting) 導致,因此你可以使用創造與執行兩階段概念來拆解理解
1 | // 原始程式碼 |
那麼這時候我們再拿六角學院 JavaScript 核心篇中的考題來看一下
1 | var a = { x: 1 }; |
基本上我們都知道 JavaScript 採用的是 Call by Sharing,因此物件會是採用傳參考概念來運作,接下來讓我們一步一步拆解上面考題
第四步驟稍微會有一點難度,我們先看 a = { y: 1 }
的部分就好
接下來讓我們先暫停一下,剛才前面有講到「Multiple left-hand assignment」的一些拆解與觀念,因此你在看最後一行 a.y = a = { y: 1};
時可能無意識會這樣拆解
1 | a = { y: 1}; |
或者是這樣
1 | a.y = (a = { y: 1}); |
所以你可能會很興奮的說 console.log(a);
非常簡單答案就是 { y: 1 }
但是 console.log(b)
,就稍微有一點困難了,讓我們看一下剛剛的表
我們可以發現 b
依然是指向 0x01
,而 0x01
中的 x
被指向到 0x02
,因此其中一個一定是 { x: { x: 2 } }
!
等等你發現了嗎?我為什麼會「其中一個」呢?因為這邊絕對有蹊蹺。
主要原因在於 a.y = a = { y: 1};
這一段非常陰險!因為代誌不是憨人想得那麼簡單,你可能會想說 a
因為被賦予到新的記憶體位置 0x03
,接下來又替 a
新增一個屬性叫做 y
然後指向到 0x03
的位置,畢竟你可能是這樣去拆解
1 | a.y = (a = { y: 1}); |
或者是這樣
1 | a = { y: 1 }; |
但這邊絕對是錯的,因此先讓我們先看一下 JavaScript 的優先性與相依性的部分,我們可以發現「點(.
)運算子」的優先性會非常高,所以 a.y
必定會優先被執行,而這時候的 a
並沒有被覆蓋記憶體,因此取出來的 a
會是保持原本的記憶體位置也就是 0x01
,但因為屬性還沒有給予值,所以這時候 JavaScript 會預設給予 undefined
,接下來才會「正式進入」賦值階段。
那麼在說明「賦值階段」之前請務必切記 b
目前指向的記憶體是 0x01
,因此 a
被重新賦予 { y: 1}
(新記憶體空間 0x03
),但是 0x01
並沒有被釋放掉,為什麼沒有被釋放掉呢?主要原因是 JavaScript 解析器認為你這個記憶體還有被指向著,因此 0x01
並不會被釋放,那麼以程式碼角度來看就會是這樣
1 | a = { y: 1} (0x03); |
所以最終的 console.log(b)
答案就會是 { x: { x:2 } y: { y:1 } }
。
1 | var a = { x: 1 }; |
如果用 AST 抽象語法樹來看的話就會是這樣
你也可以試著將語法貼到以下網址運作一次就會很清楚了