iT邦幫忙

2021 iThome 鐵人賽

DAY 11
0
自我挑戰組

JavaScript 奇奇怪怪的核心觀念系列 第 11

(Day11) 物件參考特性

傳值與傳參考

物件傳參考是 JS 中非常重要的特性,純值和物件在賦值時的行為模式不同:

  • 純值在賦值時會是傳值
  • 物件型別都是傳參考(參考記憶體)

先來看看傳值的程式碼:

var name1 = 'Ryder'
var name2 = name1
name2 = 'Jack'
console.log( name1, name2 )//Ryder , Jack 

傳參考範例

這個狀況很好理解,name1 的資料賦值給 name2,nam2 獨立修改資料,不影響 name1 ,但如果是物件的傳參考狀況呢?

var obj1 = { name:'Ryder' }
var obj2 = obj1
 obj2.name = 'Jack'
console.log( obj1.name ,obj2.name) //Jack , Jack 

答案會是兩邊都會是 Jack ,並且若再使用 console.log(obj1 === obj2) 他也會回傳 true ,這是因為在 JS 中,物件賦值時是傳參考的。

那什麼是傳參考呢?

根據上面程式碼,一行一行來講解

  1. 首先再建立一個物件時,該物件便會創建一個記憶體,也就是範例中的:
var obj1 = { name:'Ryder' }

這樣物件本就會有一個獨立的記憶體,目前稱做 00x1,情況大致如圖:

  1. 再來 obj2 = obj1obj1 賦值給 obj2 時,其實這段就是提供物件參考的記憶體,因此稱作傳參考,情況如圖:

    也因為 obj1obj2 的記憶體指向是相同記憶體,因此當我們使用 obj2.name 修改時,兩個物件的內容都會被修改。

物件實字 {} 建立新記憶體

再來看看例外狀況:
試著為 obj2 賦予新物件:

var obj1 = { name:'Ryder' }

var obj2 = obj1
obj2 = { name:'Ryder' }
connsole.log(obj1 === obj2) //false

結果之所以回傳 false 是因為 obj2 有在使用物件實字 {} 建立一個新物件,此時會生成一個新的記憶體指向,用圖表示就是:

特殊範例

最後來看看一個狀況特別的延伸的範例:

var obj1 = { a:1 }
var obj2 = obj1
obj1.a = { a:2 }
obj1.b = obj1 = { b:1 } 
obj1 //{b: 1}
obj2 //{a: {a:2}, b: {b:1}}

這邊出現的結果不論是 obj1obj2 都令人疑惑,這邊一行一行來說明:

var obj1 = { a:1 }
  1. obj1 創立一個物件此時誕生一個新記憶體 00x1 ,內容是 { a:1 } ,狀況如圖:

  2. obj2 賦值實際獲得的是 00x1 記憶體,如圖:

  3. obj1 中的 a 屬性從原本純值 1 替換成新物件 { a:2 },此時也會建立新記憶體 00x2 ,如圖:

  4. 接下來是重點 obj1.b = obj1 = { b:1 } 根據運算子相依性特性 obj1 = { b:1 } 會先執行,同時我們也看到 obj1 重新賦值一個新物件,因此會誕生一個新記憶體 00x3 ,並且 obj1 指向的記憶體會被更改成 00x3,如圖:

  5. 接下來是將 obj1 = { b:1 } 這段回傳的 { b:1 } 賦予到 obj1.b 上,但要注意的是我們輸入的是 obj1.b = obj1 = { b:1 } 這段只是一行程式碼,JS 實際上在編譯時不會為了 obj1 = { b:1 } 馬上建立一段新的記憶體,因此 obj1.b 實際指向的並非是後來變更的 00x3 而是變更前的 00x1 ,因此就會是 00x1 又塞入了 00x3 內容,如圖:

    最後使用 console.log(obj1 === obj2.b) 答案也會是 true 了。

P.S. 最讓人疑問的便是 obj1.b = obj1 = { b:1 } 這段。
而這段的重點是,JS 在編譯這種一行執行的程式碼時,是不會為了個別『運算式』去建立記憶體,因此這種一行程式碼,都還是會使用原始的記憶體指向。

參考資料


上一篇
(Day10) 物件基礎介紹
下一篇
(Day12) 物件,淺拷貝/深拷貝
系列文
JavaScript 奇奇怪怪的核心觀念30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言