close

mutable和immutable是一個很重要的觀念,而且mutable和immutable其實和JavaScript的基本型別和物件型別的傳值(by value)、傳址(by reference)特型有很大的關聯,我們這就從傳值和傳址的部分開始看吧!

基本型別的傳值特性
所謂傳值(by value)?
「傳值by value」白話一點來說的話,就的是「以值為基準,而不是以址為基準,每個值都是獨立的,就算值看起來是相等的,但所佔的記憶體位置都不同」。
可能單單這樣的一句話,還是很難理解,接下來透過一些實際的情境來理解吧!


實際的程式碼情境
carbon (20)
這一段程式碼,做了幾個動作,以行為單位分別是「宣告變數」、「用既有變數來宣告新的變數(將既有變數值賦予新變數)」、「對變數重新賦值」。

宣告變數
var a = 1;
在宣告變數時,一開始會先將變數指向undefined,再進行賦值的動作。JavaScript會留一個記憶體空間給這個值,在這個情境中,以圖呈現的話,會是以下這個樣子。
(1) 一開始會先指向undefined(如果是用let或const宣告的話,這一步的狀況會有點不同,這部分可以參考之前在學習hoisting特性的筆記文)。
image

(2) 再來才真正來到賦值的動作,把這個變數a指向替1這個值保留的名為0x001記憶體空間(記憶體位址)。
image

用既有變數來宣告新變數(將既有變數值賦予新變數)
var b = a;
如果是拿既有變數來宣告新變數的話,並不會指向原本1這個值的記憶體空間,而是會將這個值複製出來,也就是說雖然現在有兩個一樣為1的值,但這兩個1的所佔的記憶體空間卻不同。
以圖呈現的話,大概會呈現如下:
(1) 變數a的值1會被複製出來,並被放在另一個記憶體空間,例如:0x002,這個1也就變成另一個獨立的1,雖然以肉眼看一樣是1,但其實不是同一個1。
image
(2) 變數b會指向這個被複製出來,並存在另一個記憶體空間的1。
image
變數重新賦值
a = 2;
在這個時候,雖然肉眼上直觀會覺得是「a被變動了,或是原本的1被變成2了」,但實際情況並不是這樣。其實在這個狀況下,會再為2這個值保留一個記憶體空間,再將變數a指向這個新的記憶體空間,如果沒有其他變數指向原本1的值,JavaScript在就會將這個值進行垃圾回收(Garbage Collection)。用圖來看的話,會是以下這個樣子。
(1) 產生一個存放數字2的記憶體空間,例如0x003。
image
(2) 將變數a指向這個存放數字2的記憶體空間。
image
(3) 如果原本數字1沒有被引用,就會被JavaScript的垃圾回收機制回收,釋放出這個空間。

這邊要記住一點,「現在出現的值,例如a原本的1和b的1和a後來變成的2都是獨立的個體,大家佔有的記憶體空間都不同,雖然有兩個1在人眼看起來是一樣的,而且在進行變動的動作時,實際上都沒有改動到原本記憶體空間的值,而是讓變數指向另一個存放新值的記憶體空間」


物件型別的傳址特性
何謂傳址(by reference)?
「傳址by reference」白話來說的話,就是「以記憶體的位址為基準,來進行修改或複製的動作,並不是以值為基準」。
這裡也用一些常見的物件操作情境來看看在物件傳址的特型上,會發生什麼事情吧!


實際的程式碼情境
carbon (47)
這一段程式碼,一樣以行為單位分別是「宣告變數」、「用既有變數來宣告新的變數(將既有變數值賦予新變數)」、「改動物件變數內的屬性的值」。

宣告變數
var a = {  
  name: 'Jolin'
};      
在宣告變數的這個動作,跟傳址的狀況基本上是一樣的,一開始會先指向一個undefined的變數,再來會幫這個物件保留一個記憶體空間,再把變數指向這個記憶體空間。
(1) 一開始一樣會先指向undefined
image
(2) 再來才真正來到賦值的動作,把這個變數a指向替1這個值保留的名為0x004記憶體空間(記憶體位址)
image

用既有變數來宣告新的變數(將既有變數值賦予新變數)
var b = a;
如果是拿既有變數來宣告新變數的話,並不會將值複製出來,存在另一個記憶體空間,而是會將這個變數指向原本的這個物件,意思也就是說,變數a和變數b是同存放在同個記憶體空間的物件。
以圖呈現的話,大概會呈現如下:
(1) 變數b會指向變數a指向的物件。
image

改動物件變數內的屬性的值
a.name = 'IU';
在這個時候,雖然肉眼上看到的是「a的name被變成IU,b並沒有被變動到」,但實際情況並不是這樣。在這個狀況下,因為a和b是指向同一個記憶體空間,也就說實際上是同一個物件,所以雖然是去改動變數a的name,實際上變數b的name也一樣有變動,因為a和b是同個物件。
(1) 透過a變數進行name的改動,因為是去改動0x004的這個記憶體空間的物件,並不是重新讓它指向另一個記憶體空間的物件值,所以一樣指向這個記憶體空間的b當然也一樣是改動後的內容。
image

這邊要記住一點,「現在出現的值,例如a原本的物件和b的物件和a後來改name的物件都是同個物件,記憶體空間皆相同,而且在進行變動值的動作並不是重新值向另一個記憶體空間,所以不管是從變數a還是從變數b進行改動的動作,都是改動到同個物件」。

mutable與immutable
從這兩個英文字的翻譯來看的話,就是「可變的」和「不可變的」,但是這裡指的可變和不可變,並不是很直覺地以肉眼看到這個變數能不能被變動為基準下去看,而是以實際上指向的值會不會被變動到下去看。
前面講了那麼多關於傳值和傳址是什麼的內容,這裡終於要說到和這次主題的關聯性了!
還記得這兩句話嗎?
傳值:「現在出現的值,例如a原本的1和b的1和a後來變成的2都是獨立的個體,大家佔有的記憶體空間都不同,雖然有兩個1在人眼看起來是一樣的,而且在進行變動的動作時,實際上都沒有改動到原本記憶體空間的值,而是讓變數指向另一個存放新值的記憶體空間」
傳址:「現在出現的值,例如a原本的物件和b的物件和a後來改name的物件都是同個物件,記憶體空間皆相同,而且在進行變動值的動作並不是重新值向另一個記憶體空間,所以不管是從變數a還是從變數b進行改動的動作,都是改動到同個物件」

這兩句話其實就包含了mutable與immutable的特性,在傳值的情境中,不管怎樣都不會改動到原本的記憶體空間的值,而是會重新指向另一個記憶體空間,這也就是所謂的immutable特性,因為你改動不到任何一個記憶體空間的值,每個值都是獨立的個體;在傳址的情境中,因為指向同一個記憶體空間的物件,改動的動作也是直接去改動到指向的物件,並不是讓它另外指向其他記憶體空間,所以不管你從哪個變數做改動的動作,都是在改動到同個物件,也就是所謂的mutable特性。

已經對mutable和immutable的特性是什麼意思後,再來延伸看看這兩種特性各有什麼優缺點。


mutable的優缺點
優點
- 可以直接進行變動的動作,所以便利性和彈性較高。
- 在有變動的動作出現時,因為不需要產生一個新的記憶體空間,所以比較節省效能。


缺點
- 如果有太多變數指向同一個記憶體空間,當有變動時,會不好追蹤是哪個地方變動到這個值。
- 因為很好進行變動,所以比較容易不小心改動到不該改動到的變數。

immutable的優缺點
優點
- 因為不能隨意被變動,所以穩定性比較高,不易產生side effect。
缺點
- 如果有比較多的改動的動作,會因為需要額外的建立一個新的記憶體空間和執行回收機制,導致花費效能。

以上就是這次的學習筆記了!其實已經有很多人寫過相關的內容,但這次還是想要透過自己能理解的方式把這些原本沒有完全搞懂的基本概念記錄下來。透過這次的學習過程,才發現原來跟原本傳值和傳址的特性有這麼大的關聯。雖然這個部分基礎到不行,但不好好靜下心來去理解,以我的腦袋瓜,真的很容易沒有正確地理解。總之,現在終於搞懂這部份了。
就先這樣啦!打完收工!

 

arrow
arrow

    文科少女寫程式 發表在 痞客邦 留言(0) 人氣()