不可變數(shù)據(jù)類型
immutable或辖,副作用笆檀,純函數(shù)關(guān)鍵詞解釋:
- immutable: 不可變數(shù)據(jù)
- 副作用:調(diào)用函數(shù),同時(shí)對(duì)主函數(shù)產(chǎn)生了附加的影響尺铣,比如修改全局變量拴曲,改變參數(shù)。
如何避免凛忿,創(chuàng)建不可變數(shù)據(jù)類型澈灼,在一次更新過程中不應(yīng)該改變?cè)袑?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)部來判斷是否有變化從而最終決定如何返回乾闰。