搞懂JavaScript中的連續賦值
- 2019 年 12 月 5 日
- 筆記
搞懂JavaScript中的連續賦值
前段時間老是被一道題刷屏,一個關於連續賦值的坑。 遂留下一個筆記,以後再碰到有人問這個題,直接丟過去鏈接。。
題目程式碼:
let a = { n: 1 } let b = a a.x = a = { n: 2 } console.log(a.x) // => undefined console.log(b.x) // => { n: 2 }
首先解釋一下連續賦值的意思: 表達式
variable = 1
,這個為賦值語句。 當我們要給多個變數進行賦值時,有一個簡單的寫法。variable1 = variable2 = 1
,這個我們就稱之為連續賦值。
再來說上邊的那道題,我一次看到這個題的時候,答案也是錯了,後來翻閱資料,結合著調試,也算是整明白了-.-
前兩行的聲明變數並賦值,使得a
和b
都指向了同一個地址({ n: 1 }
在記憶體中的位置)
為了理解連續賦值的運行原理,我們需要結合著ECMAScript的文檔來解釋一下=
賦值的執行過程

圖中出現了一個關鍵字LeftHandSideExpression(我們簡稱為LHS
) MDN對該關鍵字的解釋為「Left values are the destination of an assignment.」
,翻譯過來大概就是:LHS
是用來分配賦值操作結果存放的位置(也就是=
右邊的這坨東西要放到哪)。
在執行一個賦值操作時,我們首先要取出=
左側的變數,用來確定這次賦值操作最終結果的存放位置。 然後運算=
右側的表達式來獲取最終的結果,並將結果存放入對應的位置,也就是前邊取出的變數所對應的位置。
再來說連續賦值,其實就是多次的賦值操作。
我們從程式碼的第一行開始,畫圖,一個圖一個圖的來說:
let a = { n: 1 }
聲明了一個變數a
,並且創建了一個Object
:{ n: 1 }
,並將該Object
在記憶體中的地址賦值到變數a
中,這時就能通過a
來獲取到{ n: 1}
:引用類型的值是只存放地址的,而不是直接存放原始值({} !== {}
)

let b = a
聲明一個變數b
,並且將a
賦值給b
,這時,a
和b
都指向了{ n: 1 }
:

- 執行表達式(
a.x = a = { n: 2 }
),取出a.x
的位置,由於a
的值為{ n: 1 }
,所以取屬性x
為undefined
,遂在記憶體中開闢一塊新的空間作為({ n: 1}).x
的位置:

- 執行剩餘表達式(
a = { n: 2 }
),取出a
的位置,因為a
是一個已聲明的變數,所以該步驟並不會有什麼改變; - 執行剩餘表達式(
{ n: 2 }
),為{ n: 2 }
在記憶體中開闢一塊空間存放數據:

- 將
{ n: 2 }
賦值到第4
步取出的a
對應的位置:

- 將
{ n: 2}
賦值到第3
步取出的a.x
對應的位置:

這時我們就完成了整個賦值步驟:
- 變數
a
指向{ n: 2 }
- 變數
b
指向{ n: 1, x: { n: 2} }
- 也就是說
a === b.x
小記
該程式碼坑就在於:賦值運算會在運算=
右側前就取出了要賦值的位置,而不是獲得結果後再去取出賦值位置的。 先取位置,後賦值 所以說,看文檔很重要 很重要 很重要