最近經過一年的淬鍊再回頭看看有關於傳值這篇文
https://blog.techbridge.cc/2018/06/23/javascript-call-by-value-or-reference/
我覺得 call by value(address) 的解釋法應該是最接近且一致的心理模型了,來分享一下我對 JavaScript 中賦值、存取與修改的看法。
我預設的心理模型:
- 識別字(identity)存取資料時是靠記憶體地址,並不是值本身。
- 重新賦值時(reassign),無論資料型別,一率另開一個記憶體地址放入該值,並儲存該記憶體地址。
- 修改物件內容時,修改的目標是該記憶體地址內的值。
- 賦值一個識別字時,會透過儲存的記憶體地址找到值:
- 若該值為原始數值(primitive value),則另開一個記憶體地址放入該值,並儲存該記憶體地址。
- 若該值為非原始數值(Non-primitive value),則直接複製原先目標的記憶體地址。
接著以這個心理模型解釋以下幾種常見的狀況:
若複製目標為原始數值並重新賦值:
1 | var primitiveValue = 1 // 賦值一個記憶體地址(0x00)裡面放入 1 |
若複製目標為非原始數值並重新賦值:
1 | var nonPrimitiveValue = {a: 1} // 賦值一個記憶體地址(0x00)裡面放入 {a: 1} |
若複製目標為非原始數值並修改:
1 | var nonPrimitiveValue = {a: 1} // 賦值一個記憶體地址(0x00)裡面放入 {a: 1} |
傳入函式中並重新賦值
1 | var nonPrimitiveValue = {a: 1} // (1.) 分配至 0x00,value 為 {a: 1} |
傳入函式中並修改
1 | var nonPrimitiveValue = {a: 1} // (1.) 分配至 0x00,value 為 {a: 1} |
以上通通是用同個心理模型來解釋,所以我目前斷言 JavaScript 最主要是 call by value 而這個 value 指的是記憶體位置的部分。
而對於「賦值」為什麼會有情況原因在於「重新賦值」與「修改」的差別
- 重新賦值(reassign):給識別字(Identifer)一個新的記憶體地址
- 修改:從原先的記憶體地址中找到值再做修改,也就是物件成員表達式(MemberExpression)中的值。
目前驗證的方法可以透過這個簡易的視覺化工具執行編譯分析,去觀察直接讀取識別字跟識別字成員的差別。
以上是我對於賦值、存取與修改的看法,也歡迎不同的意見來交流與驗證!