摘要:文章博客地址所創(chuàng)建的數(shù)據(jù)有一個迷人的特性數(shù)據(jù)創(chuàng)建后不會被改變。是的基類,使用該類時需要至少繼承其子類中的一個。總結(jié)所提供的和固有的各有優(yōu)勢,未來有可能制定一套原生的規(guī)范,在這之前,是一個不錯的選擇。參考資料官方文檔
文章博客地址:http://pinggod.com/2016/Immutable/
Immutable.js 所創(chuàng)建的數(shù)據(jù)有一個迷人的特性:數(shù)據(jù)創(chuàng)建后不會被改變。我們使用 Immutable.js 的示例來解釋這一特性:
var Immutable = require("immutable"); var map1 = Immutable.Map({a:1, b:2, c:3}); var map2 = map1.set("b", 50); map1.get("b"); // 2 map2.get("b"); // 50
在上面代碼第三行中,map1 使用 set 方法更新數(shù)據(jù),結(jié)果返回一個新的 Map 類型數(shù)據(jù) map2,map2 包含了更新后的數(shù)據(jù),但是 map1 沒有發(fā)生變化。這種特性讓我們在引用數(shù)據(jù)的時候毫無后顧之憂,因為任何對數(shù)據(jù)的修改都不會影響最原始的數(shù)據(jù)。在 Immutable.js 誕生之前,我們可以使用深拷貝的方式模擬這一特性,但是會耗費過多的內(nèi)存空間和計算力。Immutable.js 相比深拷貝的優(yōu)勢在于區(qū)分發(fā)生變化的數(shù)據(jù)和未變化的數(shù)據(jù),對于上面的 map1 和 map2,b 是變化的數(shù)據(jù),所以 map1 和 map2 各保存一份 b 數(shù)據(jù),而 a 和 c 是未變化的數(shù)據(jù),所以 map1 和 map2 仍然共享 a 和 c 的數(shù)據(jù)。
概覽Immutable Data 鼓勵開發(fā)者使用純函數(shù)式的開發(fā)方式,并從函數(shù)式開發(fā)中引入了惰性計算的特性。雖然加入了很多函數(shù)式的概念,Immutable.js 仍然提供了類似原生 JavaScript Array、Map 和 Set 中的方法,并且提供了在原生 JavasScript 數(shù)據(jù)和 Immutable 數(shù)據(jù)之間快速轉(zhuǎn)換的機制。
Immutable.js 的 API 主要包含以下幾部分:
formJS(),將 JavaScript Object 和 Array 徹底轉(zhuǎn)換為 Immutable Map 和 List
is(),與 Object.is() 類似都是對值的比較,但它會將 Immutable Iterable 視為值類型數(shù)據(jù)而不是引用類型數(shù)據(jù),如果兩個 Immutable Iterable 的值相等,則返回 true。與 Object.is() 不同的是,is(0, -0) 的結(jié)果為 true
List,有序索引集,類似于 JavaScript 中的 Array
Map,無序 Iterable,讀寫 Key 的復(fù)雜度為 O(log32 N)
OrderedMap,有序 Map,排序依據(jù)是數(shù)據(jù)的 set() 操作
Set,元素為獨一無二的集合,添加數(shù)據(jù)和判斷數(shù)據(jù)是否存在的復(fù)雜度為 O(log32 N)
OrderedSet,有序 Set,排序依據(jù)是數(shù)據(jù)的 add 操作。
Stack,有序集合,且使用 unshift(v) 和 shift() 進行添加和刪除操作的復(fù)雜度為 O(1)
Range(),返回一個 Seq.Indexed 類型的數(shù)據(jù)集合,該方法接收三個參數(shù) (start = 1, end = infinity, step = 1),分別表示起始點、終止點和步長,如果 start 等于 end,則返回空的數(shù)據(jù)結(jié)合
Repeat(),返回一個 Seq.indexed 類型的數(shù)據(jù)結(jié)合,該方法接收兩個參數(shù) (value,times),value 表示重復(fù)生成的值,times 表示重復(fù)生成的次數(shù),如果沒有指定 times,則表示生成的 Seq 包含無限個 value
Record,用于衍生新的 Record 類,進而生成 Record 實例。Record 實例類似于 JavaScript 中的 Object 實例,但只接收特定的字符串作為 key,且擁有默認(rèn)值
Seq,序列(may not be backed by a concrete data structure)
Iterable,可以被迭代的 (Key, Value) 鍵值對集合,是 Immutable.js 中其他所有集合的基類,為其他所有集合提供了 基礎(chǔ)的 Iterable 操作函數(shù)(比如 map() 和 filter)
Collection,創(chuàng)建 Immutable 數(shù)據(jù)結(jié)構(gòu)的最基礎(chǔ)的抽象類,不能直接構(gòu)造該類型
1. fromJS()Immutable.fromJS({a: {b: [10, 20, 30]}, c: 40}, function (key, value) { var isIndexed = Immutable.Iterable.isIndexed(value); return isIndexed ? value.toList() : value.toOrderedMap(); }); // true, "b", {b: [10, 20, 30]} // false, "a", {a: {b: [10, 20, 30]}, c: 40} // false, "", {"": {a: {b: [10, 20, 30]}, c: 40}}
fromJS() 的使用方式類似于 JSON.parse(),接收兩個參數(shù):json 數(shù)據(jù)和 reviver 函數(shù)。
2. ListList(): List List (iter: Iterable.Indexed ): List List (iter: Iterable.Set ): List List (iter: Iterable.Keyed ): List List (array: Array ): List List (iterator: Iterator ): List List (iterable: Object): List
List() 是一個構(gòu)造方法,可以用于創(chuàng)建新的 List 數(shù)據(jù)類型,上面代碼演示了該構(gòu)造方法接收的參數(shù)類型,此外 List 擁有兩個靜態(tài)方法:
List.isList(value),判斷 value 是否是 List 類型
List.of(...values),創(chuàng)建包含 ...values 的列表
下面演示幾個 List 常用的操作,更詳細的 API 說明請參考官方文檔:
// 1. 查看 List 長度 const $arr1 = List([1, 2, 3]); $arr1.size // => 3 // 2. 添加或替換 List 實例中的元素 // set(index: number, value: T) // 將 index 位置的元素替換為 value,即使索引越界也是安全的 const $arr2 = $arr1.set(-1, 0); // => [1, 2, 0] const $arr3 = $arr1.set(4, 0); // => [ 1, 2, 3, undefined, 0 ] // 3. 刪除 List 實例中的元素 // delete(index: number) // 刪除 index 位置的元素 const $arr4 = $arr1.delete(1); // => [ 1, 3 ] // 4. 向 List 插入元素 // insert(index: number, value: T) // 向 index 位置插入 value const $arr5 = $arr1.insert(1, 1.5); // => [ 1, 1.5, 2, 3 ] // 5. 清空 List // clear() const $arr6 = $arr1.clear(); // => []3. Map
Map 可以使用任何類型的數(shù)據(jù)作為 Key 值,并使用 Immutable.is() 方法來比較兩個 Key 值是否相等:
Map().set(List.of(1), "listofone").get(List.of(1)); // => "listofone"
但是使用 JavaScript 中的引用類型數(shù)據(jù)(對象、數(shù)組)作為 Key 值時,雖然有時兩個 Key 很像,但它們也是兩個不同的 Key 值:
console.log(Map().set({}, 1).get({})) // => undefined
Map() 是 Map 類型的構(gòu)造方法,行為類似于 List(),用于創(chuàng)建新的 Map 實例,此外,還包含兩個靜態(tài)方法:Map.isMap() 和 Map.of()。下面介紹幾個 Map 實例的常用操作,更詳細的 API 使用說明請參考官方文檔:
// 1. Map 實例的大小 const $map1 = Map({ a: 1 }); $map1.size // => 1 // 2. 添加或替換 Map 實例中的元素 // set(key: K, value: V) const $map2 = $map1.set("a", 2); // => Map { "a": 2 } // 3. 刪除元素 // delete(key: K) const $map3 = $map1.delete("a"); // => Map {} // 4. 清空 Map 實例 const $map4 = $map1.clear(); // => Map {} // 5. 更新 Map 元素 // update(updater: (value: Map) => Map ) // update(key: K, updater: (value: V) => V) // update(key: K, notSetValue: V, updater: (value: V) => V) const $map5 = $map1.update("a", () => (2)) // => Map { "a": 2 } // 6. 合并 Map 實例 const $map6 = Map({ b: 2 }); $map1.merge($map6); // => Map { "a": 1, "b": 2 }
OrderedMap 是 Map 的變體,它除了具有 Map 的特性外,還具有順序性,當(dāng)開發(fā)者遍歷 OrderedMap 的實例時,遍歷順序為該實例中元素的聲明、添加順序。
4. SetSet 和 ES6 中的 Set 類似,都是沒有重復(fù)值的集合,OrderedSet 是 Set 的遍歷,可以保證遍歷的順序性。
// 1. 創(chuàng)建 Set 實例 const $set1 = Set([1, 2, 3]); // => Set { 1, 2, 3 } // 2. 添加元素 const $set2 = $set1.add(1).add(4); // => Set { 1, 2, 3, 4 } // 3. 刪除元素 const $set3 = $set1.delete(3); // => Set { 1, 2 } // 4. 并集 const $set4 = Set([2, 3, 4, 5, 6]); $set1.union($set1); // => Set { 1, 2, 3, 4, 5, 6 } // 5. 交集 $set1.intersect($set4); // => Set { 3, 2 } // 6. 差集 $set1.subtract($set4); // => Set { 1 }5. Stack
Stack 是基于 Signle-Linked List 實現(xiàn)的可索引集合,使用 unshift(v) 和 shift() 執(zhí)行添加和刪除元素的復(fù)雜度為 O(1)。
// 1. 創(chuàng)建 Stack 實例 const $stack1 = Stack([1, 2, 3]); // => Stack [ 1, 2, 3 ] // 2. 取第一個元素 $stack1.peek() // => 1 // 2. 取任意位置元素 $stack1.get(2) // => 3 // 3. 判斷是否存在 $stack1.has(10) // => false6. Range() 和 Repeat()
Range(start?, end?, step?) 接收三個可選參數(shù),使用方法如下:
// 1. 不傳參 Range(); // => Range [ 0...Infinity ] // 2. 設(shè)置 start 起點 Range(10); // => Range [ 10...Infinity ] // 3. 設(shè)置 start 起點和 end 終點 Range(10, 20); // => Range [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ] // 4. 設(shè)置 start 起點、end 終點和 step 步長 Range(10, 20, 3); // => Range [ 10, 13, 16, 19 ]
Repeat(value, times?) 接收兩個參數(shù),其中 times 重復(fù)次數(shù)是可選參數(shù):
Repeat("foo"); // => Repeat [ foo Infinity times ] Repeat("foo", 3); // => Repeat [ foo 3 times ]
類似 Range() 和 Repeat(value) 這樣生成無限長度集合的操作,內(nèi)部都存在惰性計算的機制,只有真實取值時才會生成相應(yīng)的結(jié)果。使用 ES6 中的 Generator 函數(shù),可以輕松實現(xiàn)一個惰性計算:
function* bigArr() { for (let i = 0; i < 100000; i++) { console.log(`bigArr(${i}): ${i}`) yield i; } } const arr = bigArr(); for (let i = 0; i < 10; i++) { console.log(arr.next()); } // bigArr(0): 0 // => { value: 0, done: false } // => bigArr(1): 1 // => { value: 1, done: false } // => bigArr(2): 2 // => { value: 2, done: false } // => bigArr(3): 3 // => { value: 3, done: false } // => bigArr(4): 4 // => { value: 4, done: false } // => bigArr(5): 5 // => { value: 5, done: false } // => bigArr(6): 6 // => { value: 6, done: false } // => bigArr(7): 7 // => { value: 7, done: false } // => bigArr(8): 8 // => { value: 8, done: false } // => bigArr(9): 9 // => { value: 9, done: false }7. Record
Record 在表現(xiàn)上類似于 ES6 中的 Class,但在某些細節(jié)上還有所不同。通過 Record() 可以創(chuàng)建一個新的 Record 類,使用該類可以創(chuàng)建具體的 Record 實例,該實例包含在 Record() 構(gòu)造函數(shù)中聲明的所有屬性和默認(rèn)值。如果 Record 實例中的某個屬性被刪除了,則只會講實例中的屬性值恢復(fù)為默認(rèn)值:
// 1. 創(chuàng)建 Record 實例 const A = Record({ a: 1, b: 2 }); const r = new A({ a: 3 }); // => Record { "a": 3, "b": 2 } // 2. 刪除實例屬性 const rr = r.remove("a"); // => Record { "a": 1, "b": 2 }
此外,Record 實例還具有擴展性:
class ABRecord extends Record({a:1,b:2}) { getAB() { return this.a + this.b; } } var myRecord = new ABRecord({b: 3}) myRecord.getAB() // => 48. Seq
Seq 有兩個特點:immutable,一旦創(chuàng)建就不能被修改;lazy,惰性求值。在下面的代碼中,雖然組合了多種遍歷操作,但實際上并不會有任何的求值操作,只是純粹的聲明一個 Seq:
var oddSquares = Immutable.Seq.of(1,2,3,4,5,6,7,8) .filter(x => x % 2) .map(x => x * x);
如果要從 oddSquares 中取出索引為 1 的元素,則執(zhí)行過程為:
console.log(oddSquares.get(1)); // filter(1) // filter(2) // filter(3) // map(3) // => 9
Seq() 是 Seq 的構(gòu)造方法,它根據(jù)傳入的參數(shù)類型,輸出響應(yīng)的 Seq 類型:
輸入 Seq,輸出 Seq
輸入 Iterable,輸出同類型的 Seq(Keyed, Indexed, Set)
輸入 Array-like,輸出 Seq.Indexed
輸入附加 Iterator 的 Object,輸出 Seq.Indexed
輸入 Iterator,輸出 Seq。indexed
輸入 Object,輸出 Seq.Keyed
默認(rèn)情況下 Seq 的惰性計算結(jié)果不會被緩存,比如在下面的代碼中,由于每個 join() 都會遍歷執(zhí)行 map,所以 map 總共執(zhí)行了六次:
var squares = Seq.of(1,2,3).map(x => x * x); squares.join() + squares.join();
如果開發(fā)者知道 Seq 的結(jié)果會被反復(fù)用到,那么就可以使用 cacheResult() 將惰性計算的結(jié)果保存到內(nèi)存中:
var squares = Seq.of(1,2,3).map(x => x * x).cacheResult(); squares.join() + squares.join();9. Iterable 和 Collection
Iterable 是鍵值對形式的集合,其實例可以執(zhí)行遍歷操作,是 immutable.js 中其他數(shù)據(jù)類型的基類,所有擴展自 Iterable 的數(shù)據(jù)類型都可以使用 Iterable 所聲明的方法,比如 map 和 filter 等。
Collection 是 Concrete Data Structure 的基類,使用該類時需要至少繼承其子類中的一個:Collection.Keyed / Collection.Indexed / Collection.Set。
React在 React 官方文檔的《Advanced Performance》 一節(jié)中,專門對 React 的性能瓶頸、優(yōu)化方式做了詳細的解析。當(dāng)一個 React 組件的 props 和 state 發(fā)生變化時,React 會根據(jù)變化后的 props 和 state 創(chuàng)建一個新的 virtual DOM,然后比較新舊兩個 vritual DOM 是否一致,只有當(dāng)兩者不同時,React 才會將 virtual DOM 渲染真實的 DOM 結(jié)點,而對 React 進行性能優(yōu)化的核心就是減少渲染真實 DOM 結(jié)點的頻率,間接地指出開發(fā)者應(yīng)該準(zhǔn)確判斷 props 和 state 是否真正發(fā)生了變化。
在比對新舊 vritual DOM 和渲染真實 DOM 前,React 為開發(fā)者提供了 shouldComponentUpdate() 方法中斷接下來的比對和渲染操作,默認(rèn)情況下,該方法總會返回 true,如果它返回 false,則不執(zhí)行比對和渲染操作:
// 最簡單的實現(xiàn): shouldComponentUpdate (nextProps) { return this.props.value !== nextProps.value; }
看起來挺簡單,實在不然。當(dāng)我們需要比對的值是對象、數(shù)組等引用值時,就會出現(xiàn)問題:
// 假設(shè) this.props.value 是 { foo: "bar" } // 假設(shè) nextProps.value 是 { foo: "bar" }, // 顯然這兩者引用的內(nèi)存地址不同,但它們具有相同的值,這種時候不應(yīng)該繼續(xù)執(zhí)行渲染 this.props.value !== nextProps.value; // true
如果數(shù)據(jù)是 Immutable Data 的話,那么數(shù)據(jù)發(fā)生變化就會生成新的對象,開發(fā)者只需要檢查對象應(yīng)用是否發(fā)生變化即可:
var SomeRecord = Immutable.Record({ foo: null }); var x = new SomeRecord({ foo: "bar" }); var y = x.set("foo", "baz"); x === y; // false
處理這一問題的另一種方式是通過 setter 設(shè)置 flag 對臟數(shù)據(jù)進行檢查,但冗雜的代碼是在讓人頭疼。
總結(jié)Immutable.js 所提供的 Immutable Data 和 JavaScript 固有的 Mutable Data 各有優(yōu)勢,未來 ECAMScript 有可能制定一套原生的 Immutable Data 規(guī)范,在這之前,Immutable.js 是一個不錯的選擇。之前已經(jīng)寫文章熟悉過 Lodash 這一工具庫,Immutable 內(nèi)部也封裝了諸多常用的數(shù)據(jù)操作函數(shù),所以如果讓我來選擇的話,在 React 技術(shù)棧中我會更偏愛 Immutable。
Immutable.js 官方文檔
React: Advanced Performance
Introduction to Immutable.js and Functional Programming Concepts
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/79959.html
摘要:介紹按照官網(wǎng)的定義是指一旦被創(chuàng)造后,就不可以被改變的數(shù)據(jù)。很多場景下,對于屬性相同的對象,我們希望相等性判斷為。和都有一個名為方法,用來處理深度對象的比較。之于的好處眾所周知,性能優(yōu)化的核心在于處理方法來避免不必要的渲染。 介紹 按照官網(wǎng)的定義, Immutable Data是指一旦被創(chuàng)造后,就不可以被改變的數(shù)據(jù)。 相等性判斷 JavaScript提供三種不同的值比較操作: 嚴(yán)格相等...
摘要:函數(shù)式編程,一看這個詞,簡直就是學(xué)院派的典范。所以這期周刊,我們就重點引入的函數(shù)式編程,淺入淺出,一窺函數(shù)式編程的思想,可能讓你對編程語言的理解更加融會貫通一些。但從根本上來說,函數(shù)式編程就是關(guān)于如使用通用的可復(fù)用函數(shù)進行組合編程。 showImg(https://segmentfault.com/img/bVGQuc); 函數(shù)式編程(Functional Programming),一...
閱讀 2031·2023-04-26 00:16
閱讀 3482·2021-11-15 11:38
閱讀 3175·2019-08-30 12:50
閱讀 3183·2019-08-29 13:59
閱讀 756·2019-08-29 13:54
閱讀 2503·2019-08-29 13:42
閱讀 3310·2019-08-26 11:45
閱讀 2193·2019-08-26 11:36