Immutable 在 JavaScript 中的應(yīng)用
Mutable 對(duì)象
在 JavaScript 中,對(duì)象是引用類(lèi)型的數(shù)據(jù),其優(yōu)點(diǎn)在于頻繁的修改對(duì)象時(shí)都是在原對(duì)象的基礎(chǔ)上修改,并不需要重新創(chuàng)建,這樣可以有效的利用內(nèi)存,不會(huì)造成內(nèi)存空間的浪費(fèi),對(duì)象的這種特性可以稱(chēng)之為 Mutable,中文的字面意思是「可變」。
對(duì)于 Mutable 的對(duì)象,其靈活多變的優(yōu)點(diǎn)有時(shí)可能會(huì)成為其缺點(diǎn),越是靈活多變的數(shù)據(jù)越是不好控制,對(duì)于一個(gè)復(fù)雜結(jié)構(gòu)的對(duì)象來(lái)說(shuō),一不小心就在某個(gè)不經(jīng)意間修改了數(shù)據(jù),假如該對(duì)象又在多個(gè)作用域中用到,此時(shí)很難預(yù)見(jiàn)到數(shù)據(jù)是否改變以及何時(shí)改變的。
var obj = { /* 一個(gè)復(fù)雜結(jié)構(gòu)的對(duì)象 */ };
doSomething(obj);
// 上面的函數(shù)之行完后,此時(shí)的 obj 還是最初的那個(gè) obj 嗎?
針對(duì)這種問(wèn)題,常規(guī)的解決辦法可以通過(guò)將對(duì)象進(jìn)行深拷貝的形式復(fù)制出一個(gè)新的對(duì)象,再在新對(duì)象上做修改的操作,這樣能確保數(shù)據(jù)的可控性,但是頻繁的復(fù)制會(huì)造成內(nèi)存空間的大量浪費(fèi)。
var obj = { /* 一個(gè)復(fù)雜結(jié)構(gòu)的對(duì)象 */ };
// copy 出一個(gè)新的 obj2
// 但是 copy 操作會(huì)浪費(fèi)內(nèi)存空間
var obj2 = deepClone(obj);
doSomething(obj2);
// 上面的函數(shù)之行完后,無(wú)論 obj2 是否變化,obj 肯定還是原來(lái)那個(gè) obj
Immutable 對(duì)象
為了能更好的解決上述的問(wèn)題,出現(xiàn)了 Immutable 對(duì)象,Immutable 從字面上翻譯成中文是「不可變」。每次修改一個(gè) Immutable 對(duì)象時(shí)都會(huì)創(chuàng)建一個(gè)新的不可變的對(duì)象,在新對(duì)象上操作并不會(huì)影響到原對(duì)象的數(shù)據(jù)。這種特殊的對(duì)象并不是 JavaScript 新出的功能特性,而是業(yè)界為了解決這種問(wèn)題提供的一套解決方案,并且涌現(xiàn)出了一些優(yōu)秀的開(kāi)源類(lèi)庫(kù),其中最有名的就是 Facebook 的 Lee Byron 開(kāi)源的 immutable.js。當(dāng)然,Immutable 的這種解決方案并不是獨(dú)創(chuàng)的,而是來(lái)源于 Clojure 和 Scala。
Mutable 和 Immutable 的性能對(duì)比
對(duì)于 Mutable 的對(duì)象的低效率操作主要體現(xiàn)在復(fù)制和比較上,而 Immutable 對(duì)象就是解決了這兩大低效的痛點(diǎn)。
普通的 Mutable 對(duì)象的深拷貝操作會(huì)將一整份數(shù)據(jù)都復(fù)制一遍,而 Immutable 對(duì)象在修改數(shù)據(jù)時(shí)并不會(huì)復(fù)制一整份數(shù)據(jù),而是將變化的節(jié)點(diǎn)與未變化的節(jié)點(diǎn)的父子關(guān)系轉(zhuǎn)移到一個(gè)新節(jié)點(diǎn)上,類(lèi)似于鏈表的結(jié)構(gòu)。從 “復(fù)制” 的角度來(lái)看,做到了最小化的復(fù)制,未變化的部分都是共享的,Mutable 在復(fù)制的時(shí)候是 “全量”,而 Immutable 復(fù)制的是 “增量”,對(duì)于內(nèi)存空間的使用率的比較高低立判。
并且基于每次修改一個(gè) Immutable 對(duì)象都會(huì)創(chuàng)建一個(gè)新的 Immutable 對(duì)象的這種特性可以將數(shù)據(jù)的修改狀態(tài)保存成一組快照,這也是挺方便的。
再來(lái)說(shuō)說(shuō)比較操作。對(duì)于 Mutable 的對(duì)象,如果要比較兩個(gè)對(duì)象是否相等,必須遍歷對(duì)象的每個(gè)節(jié)點(diǎn)進(jìn)行比較,對(duì)于結(jié)構(gòu)復(fù)雜的對(duì)象來(lái)說(shuō),其效率肯定高不到哪去。對(duì)于 Immutable 對(duì)象,immutable.js 提供了直接判斷兩個(gè) Immutable 對(duì)象的「值」是否相等的 API。
var map1 = Immutable.Map({a:1, b:1, c:1});
var map2 = Immutable.Map({a:1, b:1, c:1});
assert(map1 !== map2); // 不同的 Immutable 實(shí)例,此時(shí)比較的是引用地址
assert(Immutable.is(map1, map2)); // map1 和 map2 的值相等,比較的是值
assert(map1.equals(map2)); // 與 Immutable.is 的作用一樣
在實(shí)際的開(kāi)發(fā)應(yīng)用中,性能并不總是最關(guān)鍵和重要的,對(duì)于普通的 JavaScript 的項(xiàng)目來(lái)說(shuō),由于 Immutable 的特性帶來(lái)的數(shù)據(jù)的可控性比起性能來(lái)說(shuō)更有優(yōu)勢(shì),對(duì)于 Mutable 對(duì)象適合在封閉的作用域小范圍使用,而 Immutable 對(duì)象適合數(shù)據(jù)需要跨多個(gè)作用域傳遞時(shí)使用。
Mutable 和 Immutable 在使用上的區(qū)別
immutable.js 提供了多種 Immutable 的數(shù)據(jù)結(jié)構(gòu):包含了 List Stack Map OrderedMap Set OrderedSet Record,這些數(shù)據(jù)結(jié)構(gòu)與原生的 Mutable 的數(shù)據(jù)結(jié)構(gòu)大致對(duì)應(yīng)。
各數(shù)據(jù)結(jié)構(gòu)的用法這里不細(xì)說(shuō),主要說(shuō)說(shuō) Immutable 對(duì)象與 Mutable 對(duì)象在使用上的區(qū)別吧。
原生的 Mutable 對(duì)象在「讀」和「寫(xiě)」上非常方便。
var mutableObj = {};
// 寫(xiě)入數(shù)據(jù)
mutableObj.foo = 'bar';
// 讀取數(shù)據(jù)
console.log(mutableObj.foo);
而 Immutable 對(duì)象需要通過(guò) set 和 get 來(lái)對(duì)數(shù)據(jù)進(jìn)行「讀」和「寫(xiě)」。
var immutableObj1 = Immutable.Map();
// 寫(xiě)入數(shù)據(jù)
var immutableObj2 = immutableObj1.set('foo', 'bar');
// 讀取數(shù)據(jù)
console.log(immutableObj2.get('foo')); // => 'bar'
上面的例子為了說(shuō)明 set 方法的使用才在一開(kāi)始創(chuàng)建了一個(gè)空對(duì)象,實(shí)際上可以在實(shí)例化的時(shí)候傳初始值。
var immutableObj = Immutable.Map({'foo', 'bar'});
對(duì)于層級(jí)比較深的數(shù)據(jù),immutable.js 提供的訪問(wèn)接口很方便。
var immutableObj1 = Immutable.fromJS({
a: {
b: 'c'
},
d: [1, 2, 3]
});
// 讀取深層級(jí)的數(shù)據(jù)
console.log(immutableObj1.getIn(['a', 'b'])); // => 'c'
console.log(immutableObj1.getIn(['d', 1])); // => 2
// 修改深層級(jí)的數(shù)據(jù)
var immutableObj2 = immutableObj1.setIn(['a', 'b'], 'd');
console.log(immutableObj2.getIn(['a', 'b'])); // => 'd'
如果是原生的 Mutable 對(duì)象,在鏈?zhǔn)皆L問(wèn)一個(gè)深層級(jí)的數(shù)據(jù)時(shí)可能會(huì)報(bào)對(duì)象 undefined 的錯(cuò)誤,而 Immutable 對(duì)象在碰到這種情況時(shí)不會(huì)報(bào)錯(cuò),返回的是 undefined。
在調(diào)試的時(shí)候,如果想查看一個(gè) Immutable 對(duì)象的內(nèi)部結(jié)構(gòu),建議使用 toJSON() 先轉(zhuǎn)換為普通的 Mutable 對(duì)象。
相關(guān)文章
關(guān)于COOKIE個(gè)數(shù)與大小的問(wèn)題
在一次面試過(guò)程中,面試官問(wèn)過(guò)我關(guān)于瀏覽器cookie的問(wèn)題包括:cookie大小,cookie個(gè)數(shù)限制以及如何操作cookie等一系列的問(wèn)題。2011-01-01
JavaScript實(shí)現(xiàn)數(shù)據(jù)類(lèi)型的相互轉(zhuǎn)換
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)數(shù)據(jù)類(lèi)型的相互轉(zhuǎn)換,感興趣的朋友可以參考一下2016-03-03
比較簡(jiǎn)單實(shí)用的使用正則三種版本的js去空格處理方法
比較簡(jiǎn)單實(shí)用的使用正則三種版本的js去空格處理方法...2007-11-11
前端實(shí)現(xiàn)HTML網(wǎng)頁(yè)轉(zhuǎn)PDF并導(dǎo)出
這篇文章主要為大家詳細(xì)介紹了前端如何通過(guò)html2canvas和jsPDF實(shí)現(xiàn)HTML網(wǎng)頁(yè)轉(zhuǎn)PDF并導(dǎo)出,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以參考一下2025-01-01
基于JavaScript實(shí)現(xiàn)移動(dòng)端TAB觸屏切換效果
我們使用移動(dòng)端時(shí)可以通過(guò)觸屏手勢(shì)左右滑動(dòng)來(lái)切換TAB欄目,如網(wǎng)易新聞等APP欄目切換。我們說(shuō)的TAB一般由導(dǎo)航條和TAB對(duì)應(yīng)的內(nèi)容組成,切換導(dǎo)航條上的標(biāo)簽同時(shí)標(biāo)簽對(duì)應(yīng)的內(nèi)容也會(huì)跟著切換。本文將結(jié)合實(shí)例給大家介紹一個(gè)移動(dòng)端TAB觸屏切換效果。2015-10-10
JavaScript操作DOM元素的childNodes和children區(qū)別
這篇文章主要介紹了JavaScript操作DOM元素的childNodes和children區(qū)別,本文直接給出測(cè)試代碼和運(yùn)行效果來(lái)講解它們之間的區(qū)別,需要的朋友可以參考下2015-04-04
開(kāi)發(fā)跨瀏覽器javascript常見(jiàn)注意事項(xiàng)
對(duì)于javascript的開(kāi)發(fā)人員來(lái)說(shuō),多瀏覽器的支持性,一直是個(gè)問(wèn)題,每次都要經(jīng)過(guò)測(cè)試,多個(gè)瀏覽器,才能使用下面一些常見(jiàn)的一些注意事項(xiàng)。2009-01-01

