搞懂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,這個我們就稱之為連續賦值。

再來說上邊的那道題,我一次看到這個題的時候,答案也是錯了,後來翻閱資料,結合著調試,也算是整明白了-.-

前兩行的聲明變數並賦值,使得ab都指向了同一個地址({ n: 1 }在記憶體中的位置)

為了理解連續賦值的運行原理,我們需要結合著ECMAScript的文檔來解釋一下=賦值的執行過程

圖中出現了一個關鍵字LeftHandSideExpression(我們簡稱為LHS) MDN對該關鍵字的解釋為「Left values are the destination of an assignment.」,翻譯過來大概就是:LHS是用來分配賦值操作結果存放的位置(也就是=右邊的這坨東西要放到哪)。

在執行一個賦值操作時,我們首先要取出=左側的變數,用來確定這次賦值操作最終結果的存放位置。 然後運算=右側的表達式來獲取最終的結果,並將結果存放入對應的位置,也就是前邊取出的變數所對應的位置。

再來說連續賦值,其實就是多次的賦值操作。

我們從程式碼的第一行開始,畫圖,一個圖一個圖的來說:

  1. let a = { n: 1 }聲明了一個變數a,並且創建了一個Object{ n: 1 },並將該Object在記憶體中的地址賦值到變數a中,這時就能通過a來獲取到{ n: 1}引用類型的值是只存放地址的,而不是直接存放原始值({} !== {}
  1. let b = a聲明一個變數b,並且將a賦值給b,這時,ab都指向了{ n: 1 }
  1. 執行表達式(a.x = a = { n: 2 }),取出a.x的位置,由於a的值為{ n: 1 },所以取屬性xundefined,遂在記憶體中開闢一塊新的空間作為({ n: 1}).x的位置:
  1. 執行剩餘表達式(a = { n: 2 }),取出a的位置,因為a是一個已聲明的變數,所以該步驟並不會有什麼改變;
  2. 執行剩餘表達式({ n: 2 }),為{ n: 2 }在記憶體中開闢一塊空間存放數據:
  1. { n: 2 }賦值到第4步取出的a對應的位置:
  1. { n: 2}賦值到第3步取出的a.x對應的位置:

這時我們就完成了整個賦值步驟:

  • 變數a指向{ n: 2 }
  • 變數b指向{ n: 1, x: { n: 2} }
  • 也就是說a === b.x

小記

該程式碼坑就在於:賦值運算會在運算=右側前就取出了要賦值的位置,而不是獲得結果後再去取出賦值位置的。 先取位置,後賦值 所以說,看文檔很重要 很重要 很重要

參考資料