Immutability 為何重要?淺談 immutable.js 和 seamless-immutable

這陣子為了使用 react.js 重構專案,看了不少相關資料,來整理一下 immutable.js 的部份吧。以下的內容包含 immutablility、immutable.js 和 seamless-immutable。

Mutable

JavaScript在創建變數、賦值後是可變的(mutable),且在賦值後,除了基本型態外,物件是使用call by reference的方式共享資料來源,即 。例如:

var obj = {
  a: 1,
  b: 2
};

var obj1 = obj;

若設定

obj1.a = 999;

obj.a //999

改變了obj1.a的值,同時也會更改到obj.a的值。這樣共享的好處就是節省記憶體,壞處就是稍不注意會導致「改A壞B 」的棘手問題。

一般的解法就是使用「深拷貝」(deep copy)而非淺拷貝(shallow copy),讓共享變成個別擁有自己的資料來源,缺點就是浪費記憶體。

那就讓JavaScript的資料結構變成immutable來解決。

Immutable

相較於mutable,immutable就是指在創建變數、賦值後便不可改變,若對其有任何變更(例如:新增、修改、刪除),就會回傳一個新值。

目前實作immutablity的library中較著名的就是immutable.js和seamless-immutable,由於底層實作方式不同,分別有各自的專長。

immutable.js

immutable.js的優點是

效能佳 (perfermance)

immutable.js 利用結構共享(structural sharing)的方式實作persistent data structure。 所有的更新都會產生新值,但內部利用結構分享來大大減低記憶體的使用。 例如:假設要新加入一個值到一個長度為1000的陣列,它並非產生一個新的長度為1001的陣列,而是少部份的物件被建立- 更改的部份新建立節點,而沒有更改的部份仍維持共享。

簡化變異的追蹤 (mutation tracking) - Reference & Value Equality Check

在比對兩個物件是否相等時,不使用指標的比對(reference equality check),而是值的比對(value equality check)-減少了reference equality check所帶來的recursive scan,因此效能較佳。

但由於immutable.js在實作上是把javascript物件外包一層糖衣做處理,這個產生的物件是immutable物件而非一般的javascript物件,因此若其它libraby 是使用一般的js物件,交互使用上可能會產生一些困擾。

解法是使用另外的靜態類型檢查工具/系統(例如:TypeScript or Flow)或隱藏實作細節(例如:Redux)來處理。

seamless-immutable

另一個實作immutablity的方法,比起來是較為輕巧,和其它js lib的交互使用也很良好。

由於seamless-immutable是使用原生的JavaScript物件,不產生專用的資料結構,因此較輕巧、也和javascript的其他lib互動良好,但缺點就是使用shallow copy的方式產生新值來回傳,沒有structural sharing和value equality check所帶來的效能好處。

總結

Immutability讓JavaScript降低了變異的複雜度,並且擁抱了現在正夯的函數編程(functional programming)。而實作Immutability特性的library,目前最著名的就是immutable.js和seamless-immutable。如果要選一種來使用,大概可以用這樣的方式決定:希望功能完善,但不介意整合或相容(與其它library的互動)、輕巧與否議題,可選immutable.js;反之,若在意相容問題,或在乎輕巧,而不在意功能完善,那就選seamless-immutable。

參考資料/推薦閱讀


comments powered by Disqus