不可變數(shù)據(jù)類型
immutable,副作用,純函數(shù)關(guān)鍵詞解釋:
- immutable: 不可變數(shù)據(jù)
- 副作用:調(diào)用函數(shù),同時(shí)對(duì)主函數(shù)產(chǎn)生了附加的影響,比如修改全局變量,改變參數(shù)。
如何避免,創(chuàng)建不可變數(shù)據(jù)類型,在一次更新過程中不應(yīng)該改變原有對(duì)象,需要返回一個(gè)新的對(duì)象 - 純函數(shù):無副作用的函數(shù)
js 中的對(duì)象一般是可變的,因?yàn)槭褂昧艘觅x值,雖然可以節(jié)約內(nèi)存,但是會(huì)有一些隱患。一般做法使用淺拷貝和深拷貝來避免修改,但是這樣會(huì)造成 CPU 和內(nèi)存浪費(fèi)。
為了解決這個(gè)問題,出現(xiàn)了 immutable.js 和 immer
immutable.js 庫
immutable.js 實(shí)現(xiàn)原理:Persistent Data Structure(持久化數(shù)據(jù)結(jié)構(gòu))。
- 使用舊數(shù)據(jù)創(chuàng)建新數(shù)據(jù)時(shí),要保證舊數(shù)據(jù)同時(shí)可用且不變。同時(shí)為了避免 deepCopy 把所有節(jié)點(diǎn)否復(fù)制一遍帶來的性能問題, immutable 使用了
Structural Sharing(結(jié)構(gòu)共享),即如果對(duì)象樹中的一個(gè)節(jié)點(diǎn)改變,只修改這個(gè)節(jié)點(diǎn)和受它影響的父節(jié)點(diǎn),其他節(jié)點(diǎn)共享。 -
immutable-js使用了另一套數(shù)據(jù)結(jié)構(gòu) api,它會(huì)將原生數(shù)據(jù)類型都轉(zhuǎn)化為 immutable-js 內(nèi)部對(duì)象。
內(nèi)部實(shí)現(xiàn)了一套 Persistent Data Structure,還有很多易用的數(shù)據(jù)類型像 Collection、List、Map、Set、Record、Seq。有非常全面的map、filter、groupBy、reduce``find函數(shù)式操作方法。同時(shí) API 也盡量與 Object 或 Array 類似。
缺點(diǎn):
- 需要學(xué)習(xí)新的 API
- 容易與原生對(duì)象混淆。
Immutable 中的 Map 和 List 雖對(duì)應(yīng)原生 Object 和 Array,但操作非常不同,比如你要用map.get('key')而不是map.key,array.get(0)而不是array[0]。另外 Immutable 每次修改都會(huì)返回新對(duì)象,也很容易忘記賦值。
和 redux 的配合使用,redux 簡化了 Flux 中多個(gè) store 的概念,只有一個(gè) store,數(shù)據(jù)操作通過 reducer 使用。reducer 就是要接受一個(gè)純函數(shù)。
const reducer = function (state, action) {
// ...
return new_state;
};
immer
mobx 作者寫的一個(gè) immutable 庫,核心實(shí)現(xiàn)利用 es6 的 proxy。
immer 使用原生數(shù)據(jù)結(jié)構(gòu)的 api,而不像 immutable-js 轉(zhuǎn)化為內(nèi)置對(duì)象的 api。
學(xué)習(xí)成本低,就是把之前的操作放置到 produce 函數(shù)的第二參數(shù)函數(shù)中去執(zhí)行。
原理:使用了一個(gè) ES6 的新特性 Proxy 對(duì)象。深層嵌套對(duì)象的結(jié)構(gòu)化共享的處理
proxy 具體可以看 Proxy
var proxy = new Proxy(target, handler);
- target 參數(shù)表示所要攔截的目標(biāo)對(duì)象。如果沒有 Proxy 的介入,操作原來要訪問的就是這個(gè)對(duì)象
- handler 參數(shù)也是一個(gè)對(duì)象,用來定制攔截行為。對(duì)于每一個(gè)被代理的操作,需要提供一個(gè)對(duì)應(yīng)的處理函數(shù),該函數(shù)將攔截對(duì)應(yīng)的操作
Proxy 無法 polyfill,所以 immer 在不支持 Proxy 的環(huán)境中,使用 Object.defineProperty 來進(jìn)行一個(gè)兼容。
immer 維護(hù)一份 state 在內(nèi)部,劫持所有操作,內(nèi)部來判斷是否有變化從而最終決定如何返回。