簡介
這個immutable Data 是什麼鬼,有什麼優點,好處等等,我就不贅述了,這篇Immutable 詳解及 React 中實踐講的很透徹。
一個說明不可變的例子
這個可變和不可變是相對於 JavaScript原生引用型別來說的。
// 原生物件
let a1 = {
b: 1,
c: {
c1: 123
}
};
let b1 = a1;
b1.b = 2;
console.log(a1.b, b1.b); // 2, 2
console.log(a1 === b1); // true
console.log(a1.c === b1.c); // true
// immutable.js 的Map
let a2 = Immutable.fromJS({
b: 1,
c: {
c1: 123
}
});
let b2 = a2.set('b', 2);
// 對 Immutable 物件的任何修改或新增刪除操作都會返回一個新的 Immutable 物件
console.log(a2.get('b'), b2.get('b')); // 1, 2 物件 a2 的 b 值並沒有變成2。
console.log(a2 === b2); // false
//如果物件樹中一個節點發生變化,只修改這個節點和受它影響的父節點,其它節點則進行共享。
console.log(a2.get('c') === b2.get('c')); //true複製程式碼
有哪些資料型別?
List
:有序索引集,類似於 JavaScript 中的 Array
。Map
:類似於 JavaScript 中的 Object
。OrderedMap
:有序 Map
,排序依據是資料的 set()
操作。Set
:和 ES6 中的 Set
類似,都是沒有重複值的集合。OrderedSet
:Set
的變體,可以保證遍歷的順序性。排序依據是資料的 add 操作。Stack
:有序集合,且使用 unshift(v)
和 shift()
進行新增和刪除操作的複雜度為 O(1)Range()
:返回一個 Seq.Indexed
型別的資料集合,該方法接收三個引數 (start = 1, end = infinity, step = 1)
,分別表示起始點、終止點和步長,如果 start
等於 end
,則返回空的資料結合。Repeat()
:返回一個 Seq.indexed
型別的資料結合,該方法接收兩個引數 (value,times)
,value
表示重複生成的值,times 表示重複生成的次數,如果沒有指定 times
,則表示生成的 Seq
包含無限個 value
。Record
:在表現上類似於 ES6 中的 Class,但在某些細節上還有所不同。Seq
:序列(may not be backed by a concrete data structure)Iterable
:可以被迭代的 (Key, Value)
鍵值對集合,是 Immutable.js 中其他所有集合的基類,為其他所有集合提供了 基礎的 Iterable
操作函式(比如 map()
和 filter
)。Collection
:建立 Immutable 資料結構的最基礎的抽象類,不能直接構造該型別。Iterable
:可以被迭代的 (Key, Value)
鍵值對集合,是 Immutable.js 中其他所有集合的基類,為其他所有集合提供了 基礎的 Iterable
操作函式(比如 map()
和 filter
)。
好吧,上面那麼多樂行常用的也就是 List
和Map
,頂多再加個Seq
。
幾個重要的API
先來說說幾個重要的API吧,也是最常用的。
fromJS()
fromJS()
是最最最常用的將原生JS資料轉換為ImmutableJS資料的轉換方法。使用方式類似於 JSON.parse()
,接收兩個引數:json
資料和 reviver
函式。
在不傳遞reviver
函式的情況下,預設將原生JS的Array
轉為List
,Object
轉為Map
.
// 常見
const t1 = Immutable.fromJS({a: {b: [10, 20, 30]}, c: 40});
console.log(t1);
// 不常用
const t2 = Immutable.fromJS({a: {b: [10, 20, 30]}, c: 40}, function(key, value) {
// 定製轉換方式,下這種就是將Array轉換為List,Object轉換為Map
const 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}}
});
console.log(t2);複製程式碼
fromJS()
的原始碼:
function fromJS(json, converter) {
return converter ?
fromJSWith(converter, json, '', {'': json}) :
fromJSDefault(json);
}
function fromJSDefault(json) {
if (Array.isArray(json)) {
return IndexedSeq(json).map(fromJSDefault).toList();
}
if (isPlainObj(json)) {
return KeyedSeq(json).map(fromJSDefault).toMap();
}
return json;
}複製程式碼
通過原始碼可以發現其預設只轉換Object
和Array
,其他原生型別原封不動返回。
toJS()
先來看官網的一段話:
immutable資料應該被當作值而不是物件,值是表示該事件在特定時刻的狀態。這個原則對理解不可變資料的適當使用是最重要的。為了將Immutable.js資料視為值,就必須使用
Immutable.is()
函式或.equals()
方法來確定值相等,而不是確定物件引用標識的===
操作符。
所以toJS()
就是用來對兩個immutable物件進行值比較的。使用方式類似於 Object.is(obj1, obj2)
,接收兩個引數。
const map1 = Immutable.Map({a:1, b:1, c:1});
const map2 = Immutable.Map({a:1, b:1, c:1});
// 兩個不同的物件
console.log(map1 === map2); // false
// 進行值比較
console.log(Immutable.is(map1, map2)); // true
// 不僅僅只能比較ImmutableJS的型別的資料
console.log(Immutable.is(undefined, undefined)); // true
console.log(Immutable.is(null, undefined)); // false
console.log(Immutable.is(null, null)); // true
console.log(Immutable.is(NaN, NaN)); // true
// 區別於 Object.is
console.log(Object.is(0, -0) ,Immutable.is(-0, 0)); // false , true複製程式碼
原始碼:
function is(valueA, valueB) {
if (valueA === valueB || (valueA !== valueA && valueB !== valueB)) {
return true;
}
if (!valueA || !valueB) {
return false;
}
if (typeof valueA.valueOf === 'function' &&
typeof valueB.valueOf === 'function') {
valueA = valueA.valueOf();
valueB = valueB.valueOf();
if (valueA === valueB || (valueA !== valueA && valueB !== valueB)) {
return true;
}
if (!valueA || !valueB) {
return false;
}
}
if (typeof valueA.equals === 'function' &&
typeof valueB.equals === 'function' &&
valueA.equals(valueB)) {
return true;
}
return false;
}複製程式碼
上面這段原始碼寫的很精練優雅,值得學習和借鑑。
Map
Map
資料型別,對應原生 Object
陣列。最最常用的 資料結構之一,迴圈時無序(orderedMap
有序),物件的 key
可以是任意值。具體看下面的例子:
console.log(Map().set(List.of(1), 'list-of-one').get(List.of(1)));
console.log(Map().set(NaN, 'NaN').get(NaN));
console.log(Map().set(undefined, 'undefined').get(undefined));
console.log(Map().set(null, 'null').get(null));複製程式碼
簡單介紹 OrderedMap
OrderedMap
是 Map
的變體,它除了具有 Map
的特性外,還具有順序性,當開發者遍歷 OrderedMap
的例項時,遍歷順序為該例項中元素的宣告、新增順序。OrderedMap
比非有序Map
更昂貴,並且可能消耗更多的記憶體。如果真要求遍歷有序,請使用List
。
List
List 資料型別,對應原生 Array 陣列。和原生陣列,最大區別不存在'空位'。[, , , , ]
console.log(List([,,,,]).toJS());// [undefined, undefined, undefined, undefined]複製程式碼
API
我們主要介紹Map
和 List
。
建立
通過建構函式 Map()
建構函式不常用,一般都是通過Immutable.fromJS()
將一個JS原生物件
轉換為一個Immutable物件
。
Map()
/*
Map<K, V>(): Map<K, V>
Map<K, V>(iter: Iterable.Keyed<K, V>): Map<K, V>
Map<K, V>(iter: Iterable<any, Array<any>>): Map<K, V>
Map<K, V>(obj: Array<Array<any>>): Map<K, V>
Map<V>(obj: {[key: string]: V}): Map<string, V>
Map<K, V>(iterator: Iterator<Array<any>>): Map<K, V>
Map<K, V>(iterable: Object): Map<K, V>
*/
console.log(Map().toJS()); // {}
console.log(Map({key: "value"}).toJS()); // {key: "value"}複製程式碼
同Key覆蓋問題
//最後的{key: value2} 覆蓋了前面的 {key: value}
console.log(Map([["key", "value"], ["key", "value2"], ["key1", "value1"]]).toJS());// {key: "value2", key1: "value1"}複製程式碼
List()
/*
List<T>(): List<T>
List<T>(iter: Iterable.Indexed<T>): List<T>
List<T>(iter: Iterable.Set<T>): List<T>
List<K, V>(iter: Iterable.Keyed<K, V>): List<any>
List<T>(array: Array<T>): List<T>
List<T>(iterator: Iterator<T>): List<T>
List<T>(iterable: Object): List<T>
*/
console.log(List().toJS()); // []
console.log(List([1,2,3,4,{a:123}]).toJS()); // [ 1, 2, 3, 4, {a: 123}]複製程式碼
另一種方式
Map.of()
console.log(Map.of('key1','value1','key2','value2','key3','value3').toJS()); // {key1: "value1", key2: "value2", key3: "value3"}複製程式碼
List.of()
console.log(List.of({x:1}, 2, [3], 4).toJS()); // [{x:1}, 2, [3], 4]複製程式碼
判斷是否是一個Map或者List
Map判斷
判斷是否是一個Map , 對原生Object不生效
console.log(Map.isMap({})); // false
console.log(Map.isMap(Map({}))); // true複製程式碼
List判斷
判斷是否是一個List , 對原生Array不生效
console.log(List.isList([])); // false
console.log(List.isList(List([]))); // true複製程式碼
獲取大小
size
// list
console.log(List([1,2,3,4]).size);// 4
console.log(List.of(1, 2, 3, 4).size);// 4
// map
console.log(Map({key: "value2", key1: "value1"}).size);// 2
console.log(Map.of({x:1}, 2, [3], 4).size);// 2複製程式碼
count()
// map
console.log(Immutable.fromJS({key: "value2", key1: "value1"}).count());// 4
// 可以定製條件,來確定大小
console.log(Immutable.fromJS({key: 1, key1: 34}).count((value, key, obj) => {
return value > 3;
}));// 1 value大於3的有兩個
// list
console.log(Immutable.fromJS([1, 2, 5, 6]).count());// 4
// 可以制定條件,來確定 大小
console.log(Immutable.fromJS([1, 2, 5, 6]).count((value, index, array) => {
return value > 3;
}));// 2 大於3的有兩個複製程式碼
countBy()
countBy()
和count()
的區別就是它的返回值是一個物件。
// Map
console.log(Immutable.fromJS({key: 1, key1: 34}).countBy((value, key, obj) => {
return value > 3;
}).toJS());// {false: 1, true: 1}
// list
console.log(Immutable.fromJS([1, 2, 5, 6]).countBy((value, index, array) => {
return value > 3;
}).toJS());// {false: 2, true: 2}複製程式碼
ps: count()
相容惰性計算 countBy()
不相容,先不做深研究。
新增元素
Set
// Map
// 將 key 位置的元素替換為 value
const $obj1 = Map({a: {a1: 34}, b: 2, c: 3, d: 444});
console.log($obj1.set('a', 0).toJS()); // {a: 0, b: 2, c: 3, d: 444}
console.log($obj1.set('e', 99).toJS()); // {a: 1, b: 2, c: 3, d: 444, e: 99}
// List
// 將 index 位置的元素替換為 value,即使索引越界也是安全的, 空位 undefined
const $arr1 = List([1, 2, 3]);
console.log($arr1.set(-1, 0).toJS()); // [1, 2, 0] 注意-1 等效於 $arr1.set($arr1.size + -1, 0)
console.log($arr1.set(4, 0).toJS()); // [ 1, 2, 3, undefined, 0 ] 空位置為了undefined複製程式碼
setIn
// Map
console.log(Immutable.fromJS([1, 2, 3, {a: 45, b: 64}]).setIn(['3', 'a'], 1000).toJS());//[1, 2, 3, {a: 1000, b: 64}]
// List
console.log(Immutable.fromJS([1, 2, 3, {a: 45, b: 64}]).setIn(['3', 'a'], 1000).toJS());//[1, 2, 3, {a: 1000, b: 64}]複製程式碼
List 特有的新增元素
插入元素
// insert(index: number, value: T)
// 向 index 位置插入 value
console.log(Immutable.fromJS([1, 2, 3]).insert(1, 1.5).toJS()); // [ 1, 1.5, 2, 3 ]複製程式碼
設定size
預設值undefined
console.log(List([]).setSize(2).toJS()); // [undefined, undefined]複製程式碼
pop、push、shift、unshift
List
資料型別也擁有pop、push、shift、unshift
這四種操作方法,和原生Array
的四種方法使用方式一致,但唯一區別就是返回新的List
,並且不改變原來的陣列本身,而原生則是會改變元素本身。
// ImmutableJS:返回新的List,並且不改變元素本身
const $test = List([1, 2, 3, 4]);
console.log($test.pop().toJS(), $test.toJS()); // [1, 2, 3] [1, 2, 3, 4]
// 原生:返回被改變的值,改變元素本身
const test = [1, 2, 3, 4];
console.log(test.pop(), test); // 4 [1, 2, 3]複製程式碼
花樣插入
// interpose
// 插入xxx之間
console.log(Immutable.fromJS([1, 2, 5, 6]).interpose(5555).toJS()); // [1, 5555, 2, 5555, 5, 5555, 6]
// interleave
// 被操作的兩個陣列,每個的第一項、第二項、第三項... 組成新的陣列。
console.log(Immutable.fromJS([1, 2, 5, 6]).interleave(Immutable.fromJS([555, 666])).toJS()); // [1, 555, 2, 666]
// zip
// 被操作的兩個陣列,抽離第一項和第二項組成新的子陣列,放入到一個大陣列中,形成二維陣列。
console.log(Immutable.fromJS([1, 2, 5, 6]).zip(Immutable.fromJS([555, 666]).toJS())); // [ [1, 555], [2, 666]]
// 自定義插入規則。
// zipWith
console.log(Immutable.fromJS([1, 2, 5, 6]).zipWith((a, b) => {
return a + b;
}, Immutable.fromJS([555, 666]).toJS())); // [ 556, 668]複製程式碼
刪除元素
delete(key)
// List
// delete(index: number)
// 刪除 index 位置的元素
console.log(Immutable.fromJS([1, 2, 3]).delete(1).toJS(), $arr1.toJS());// [ 1, 3 ] [ 1, 2, 3]
console.log(Immutable.fromJS([1, 2, 3]).delete(77).toJS(), $arr1.toJS(), '超過範圍不會強制報錯');// [ 1, 2, 3] [ 1, 2, 3] 超過範圍不會強制報錯
// Map
console.log(Immutable.fromJS({a: {a1: 34}, b: 2, c: 3, d: 444}).delete('c').toJS(), $obj1.toJS());// {a: 1, b: 2, d: 444} {a: 1, b: 2, c: 3, d: 444}
console.log(Immutable.fromJS({a: {a1: 34}, b: 2, c: 3, d: 444}).delete('asdfasfd').toJS(), $obj1.toJS());// {a: 1, b: 2, c: 3, d: 444} {a: 1, b: 2, c: 3, d: 444}複製程式碼
deleteIn
和 setIn
使用方式一致
清空元素 lear()
// List
console.log(Immutable.fromJS([1, 2, 3]).clear().toJS());// []
// Map
console.log(Immutable.fromJS({a: {a1: 34}, b: 2, c: 3, d: 444}).clear().toJS());// {}複製程式碼
修改元素
修改某一個元素
set setIn
上面已經介紹過
update
update(key: K, notSetValue: V, updater: (value: V) => V): Map<K, V>
// List
const $arr1 = Immutable.fromJS([1, 2, 3]);
console.log($arr1.update('2', (value)=> {
return value * 2;
}).toJS(), $arr1.toJS());// [1, 2, 6] [1, 2, 3]
console.log($arr1.update('6', 1, (value)=> {
return value * 2;
}).toJS(), $arr1.toJS());// [1, 2, 3, undefined, undefined, undefined, 2] [1, 2, 3]
console.log($arr1.update('6', 0, (value)=> { // 預設值必須大於0 感覺有BUG,所以還是不要用了。
return value * 2;
}).toJS(), $arr1.toJS());// [1, 2, 3] [1, 2, 3]
// Map
const $obj1 = Immutable.fromJS({a: {a1: 34}, b: 2, c: 3, d: 444});
console.log($obj1.update('a', (value)=> {
return value * 2;
}).toJS(), $obj1.toJS());// {a: 2, b: 2, c: 3, d: 444} {a: 1, b: 2, c: 3, d: 444}
console.log($obj1.update('e', 1, (value)=> {
return value * 2;
}).toJS(), $obj1.toJS());// {a: 1, b: 2, c: 3, d: 444, e: 2} {a: 1, b: 2, c: 3, d: 444}
console.log($obj1.update('e', 0, (value)=> { // 預設值入手是number必須大於0 感覺有BUG,所以還是不要用了。
return value * 2;
}).toJS(), $obj1.toJS());// {a: 1, b: 2, c: 6, d: 444} {a: 1, b: 2, c: 3, d: 444}複製程式碼
updateIn
使用方式和setIn
一樣。
獲取某個元素值
get getIn
使用方式:get(key: number, notSetValue?: T)
// List
const $test = Immutable.fromJS([1111111, 22222, {a: 888123}]);
console.log($test.get(0)); // 1111111
// 只有陣列可以用 number 型別 的key
console.log(Immutable.fromJS({1: 'abc'}).get(1), Immutable.fromJS({1: 'abc'}).get('1'));// undefined "abc" | 只有陣列可以用 number 型別 的key
// notSetValue 預設值,瞭解
console.log($test.get(11, 'no have value')); // no have value
// getIn
console.log($test.getIn(['2', 'a'], 'child no have value')); // 888123
console.log($test.getIn(['2', 'b'], 'child no have value')); // child no have value
// Map
const $test = Immutable.fromJS({a: {a1: 222}, b: 2, c: 3, d: 444});
console.log($test.get('a')); // 1111111
// notSetValue 預設值,瞭解
console.log($test.get('v', 'no have value')); // no have value
// getIn
console.log($test.getIn(['a', 'a1'], 'child no have value')); // 222
console.log($test.getIn(['d', 'b1'], 'child no have value')); // child no have value複製程式碼
獲取頭、尾元素:
// List
const $arr1 = Immutable.fromJS([1, 2, 3]);
console.log($arr1.first());// 1
console.log($arr1.last());// 3
// Map
Immutable.fromJS({a: {a1: 34}, b: 2, c: 3, d: 444});
console.log($obj1.first());// {a1: 34}
console.log($obj1.last());// 444複製程式碼
查詢某個元素
find() findLast()
find()
、findLast()
返回 value。
// List
console.log(Immutable.fromJS([1, 2, 56, {a: {b: 111}}]).find((value, index, array) => {
return index === 3;
}).toJS());// {a: {b: 111}}
// Map
console.log(Immutable.fromJS({a: {a1: 222}, b: 2, c: 3, d: 444}).find((value, key, obj) => {
return value === 3;
}));// 3複製程式碼
findKey() findLastKey()
findKey()
、findLastKey()
返回 key
// List
console.log(Immutable.fromJS([1, 2, 3, {a: {b: 111}}]).findKey((value, index, array) => {
return index === 3;
}));// 3
// Map
console.log(Immutable.fromJS({a: {a1: 222}, b: 2, c: 3, d: 444}).findKey((value, key, obj) => {
return value === 3;
}));// c複製程式碼
findEntry() findLastEntry()
findEntry()
、findLastEntry()
返回 key:value。
// List
console.log(Immutable.fromJS([1, 2, 3, {a: {b: 111}}]).findEntry((value, index, array) => {
return index === 3;
}));// [3, Map]
// Map
console.log(Immutable.fromJS({a: {a1: 222}, b: 2, c: 3, d: 444}).findEntry((value, key, obj) => {
return Immutable.is(value, Immutable.fromJS({a1: 222}));
}));// ["a", Map]複製程式碼
keyOf() lastKeyOf()
keyOf()
、lastKeyOf()
根據 value 返回 key。
// List
console.log(Immutable.fromJS([1, 2, 3, {a: {b: 111}}]).keyOf(Immutable.fromJS({a: {b: 111}}))); // 3
console.log(Immutable.fromJS([1, 2, 3, {a: {b: 111}}]).keyOf(2)); // 1
// Map
console.log(Immutable.fromJS({a: {a1: 222}, b: 2, c: 3, d: 444}).keyOf(Immutable.fromJS({a1: 222}))); // a
console.log(Immutable.fromJS({a: {a1: 222}, b: 2, c: 3, d: 444}).keyOf(2)); // b複製程式碼
List 特有查詢某個元素
indexOf() lastIndexOf()
// 找不到 返回 -1
console.log(Immutable.fromJS([1, 2, 3, {a: {b: 111}}]).indexOf(Immutable.fromJS({a: {b: 111}}))); // 3複製程式碼
findIndex() findLastIndex()
console.log(Immutable.fromJS([1, 2, 3, {a: {b: 111}}]).findIndex((value, index, array) => {
return value/3 === 1;
})); // 2複製程式碼
查詢最大、最小元素
max()
、maxBy()
預設比較規則為>
,min()
、minBy()
預設比較規則為>
。
max()
// List
console.log(Immutable.fromJS([1, 2, 301, 88]).max()); // 301
// 自定義比較規則
console.log(Immutable.fromJS([1, 2, 301, 88]).max((valueA, valueB) => {
return valueA > valueB;
})); // 301
// Map
console.log(Immutable.fromJS({a: 8888, b: 2, c: 3, d: 444}).max()); // 8888
// 自定義比較規則
console.log(Immutable.fromJS({a: 8888, b: 2, c: 3, d: 444}).max((valueA, valueB) => {
return valueA > valueB;
})); // 8888複製程式碼
maxBy()
// List
// 自定義比較的元素
console.log(Immutable.fromJS([{a: 2}, {a: 1}, {a: 2301}, {a: 222}]).maxBy((value, index, array) => {
return value.get('a');
}).toJS());// {a: 2301}
// 自定義比較的元素,和比較規則
console.log(Immutable.fromJS([{a: 2}, {a: 1}, {a: 2301}, {a: 222}]).maxBy((value, index, array) => {
return value.get('a');
}, (valueA, valueB) => {
return valueA > valueB;
}).toJS());// {a: 2301}
// Map
// 自定義比較的元素
console.log(Immutable.fromJS({a: {a1: 222}, b: {a1: 11}, c: {a1: 33}, d: {a1: 54654}}).maxBy((value, key, obj) => {
return value.get('a1');
}).toJS());// {a1: 54654}
// 自定義比較的元素,和比較規則
console.log(Immutable.fromJS({a: {a1: 222}, b: {a1: 11}, c: {a1: 33}, d: {a1: 54654}}).maxBy((value, key, obj) => {
return value.get('a1');
}, (valueA, valueB) => {
return valueA > valueB;
}).toJS());// {a1: 54654}複製程式碼
min()
同max()
minBy()
同maxBy()
keys() values() entries()
獲取ES6 Iterable
迭代器。
// List
const $test = List([11, 22, 33, 44]);
const keys = $test.keys();
for (let i of keys) {
console.log(i);
}
const values = $test.values();
for (let i of values) {
console.log(i);
}
const entries = $test.entries();
for (let i of entries) {
console.log(i);
}
// Map
const $test = Immutable.fromJS({a: {a1: 222}, b: 2, c: 3, d: 444});
const keys = $test.keys();
for (let i of keys) {
console.log(i); // a b c d
}
const values = $test.values();
for (let i of values) {
console.log(i); // {a1: 222} 2 3 444
}
const entries = $test.entries();
for (let i of entries) {
console.log(i);// ["a", Map] ["b", 2] ["c", 3] ["d", 444]
}複製程式碼
擷取
slice()
和原生Array slice()
用法一致。
// List
console.log(Immutable.fromJS([1, 2, 3]).slice(0).toJS());// [1, 2, 3]
// Map
console.log(Immutable.fromJS({a: {a1: 34}, b: 2, c: 3, d: 444}).slice(0).toJS());// {a: Object, b: 2, c: 3, d: 444}
console.log(Immutable.fromJS({a: {a1: 34}, b: 2, c: 3, d: 444}).slice(1).toJS());// {b: 2, c: 3, d: 444}複製程式碼
rest() butLast()
// List
// rest() 返回刪除第一個元素後的 List
console.log(Immutable.fromJS([1, {a: 1}, 3, 4, 5, 6]).rest().rest().toJS()); // [{a: 1}, 3, 4, 5, 6]
// butLast() 返回刪除最後一個元素後的 List
console.log(Immutable.fromJS([1, {a: 1}, 3, 4, 5, 6]).butLast().toJS()); // [1, {a: 1}, 3, 4, 5]
// Map
// rest() 返回刪除第一個元素後的 Map
console.log(Immutable.fromJS({a: {a1: 222}, b: 2, c: 3, d: 444}).rest().rest().toJS()); // {c: 3, d: 444}
// butLast() 返回刪除最後一個元素後的 Map
console.log(Immutable.fromJS({a: {a1: 222}, b: 2, c: 3, d: 444}).butLast().toJS()); // {a: {a1: 222}, b: 2, c: 3}複製程式碼
skip() skipLast() skipWhile() skipUntil()
// List
// skip(number)
// 從頭按照條件丟擲number個元素,對剩餘元素進行擷取
// 引數 數量
console.log(Immutable.fromJS([1, {a: 1}, 3, 4, 5, 6]).skip(2).toJS()); // [3, 4, 5, 6]
// skipLast(number)
// 從尾部按照條件丟擲number個元素,對剩餘元素進行擷取
// 引數 數量
console.log(Immutable.fromJS([1, {a: 1}, 3, 4, 5, 6]).skipLast(2).toJS()); // [1, {a: 1}, 3, 4]
// skipWhile()
// 從頭開始迴圈,丟擲滿足 return 條件===true 的元素。
console.log(Immutable.fromJS([111, 33 , 22, 44, 55, 66]).skipWhile((value, index, array) => {
return value > 31;
}).toJS()); // [22, 44, 55, 66]
// skipUntil()
// 從頭開始迴圈,丟擲滿足 return 條件===false 的元素。
console.log(Immutable.fromJS([32, 33 , 40, 44, 55, 66]).skipWhile((value, index, array) => {
return value < 39;// 丟擲直到小於39的元素。
}).toJS()); // [40, 44, 55, 66]
// Map
// skip(number)
// 從頭開始迴圈,丟擲滿足 return 條件===true 的元素。
// 引數 數量
console.log(Immutable.fromJS({a: {a1: 222}, b: 2, c: 3, d: 444}).skip(2).toJS()); // {c: 3, d: 444}
// skipLast(number)
// 從尾部按照條件丟擲number個元素,對剩餘元素進行擷取
// 引數 數量
console.log(Immutable.fromJS({a: {a1: 222}, b: 2, c: 3, d: 444}).skipLast(2).toJS()); // {a: {a1: 222}, b: 2}
// skipWhile()
// 從頭開始迴圈,丟擲滿足 return 條件===true 的元素。
console.log(Immutable.fromJS({a: 1, b: 2, c: 3, d: 444}).skipWhile((value, key, obj) => {
return value === 1;
}).toJS()); // {b: 2, c: 3, d: 444}
// skipUntil()
// 從頭開始迴圈,丟擲滿足 return 條件===false 的元素。
console.log(Immutable.fromJS({a: 5, b: 2, c: 3, d: 444}).skipWhile((value, key, obj) => {
return value < 39;// 丟擲直到小於39的元素。
}).toJS()); // {d: 444}複製程式碼
take() takeLast() takeWhile() takeUntil()
// List
// take(number)
// 從頭獲取幾個複合條件的元素
// 引數 數量
console.log(Immutable.fromJS([1, {a: 1}, 3, 4, 5, 6]).take(2).toJS()); // [1, {a: 1}]
// takeLast(number)
// 從尾部獲取幾個複合條件的元素
// 引數 數量
console.log(Immutable.fromJS([1, {a: 1}, 3, 4, 5, 6]).takeLast(2).toJS()); // [5, 6]
// takeWhile()
// 從頭開始迴圈,獲取滿足 return 條件===true 的元素。
console.log(Immutable.fromJS([111, 33 , 22, 44, 55, 66]).takeWhile((value, index, array) => {
return value > 31;
}).toJS()); //[111, 33]
// takeUntil()
// 從頭開始迴圈,獲取滿足 return 條件===false 的元素。
console.log(Immutable.fromJS([32, 33 , 40, 44, 55, 66]).takeUntil((value, index, array) => {
return value > 41;
}).toJS()); //[32, 33 , 40]
// Map
// take(number)
// 從頭獲取幾個複合條件的元素
// 引數 數量
console.log(Immutable.fromJS({a: 5, b: 2, c: 3, d: 444}).take(2).toJS()); // {a: 5, b: 2}
// takeLast(number)
// 從尾部獲取幾個複合條件的元素
// 引數 數量
console.log(Immutable.fromJS({a: 5, b: 2, c: 3, d: 444}).takeLast(2).toJS()); // {c: 3, d: 444}
// takeWhile()
// 從頭開始迴圈,獲取滿足 return 條件===true 的元素。
console.log(Immutable.fromJS({a: 5, b: 2, c: 3, d: 444}).takeWhile((value, key, obj) => {
return value > 2;
}).toJS()); //{a: 5}
// takeUntil()
// 從頭開始迴圈,獲取滿足 return 條件===false 的元素。
console.log(Immutable.fromJS({a: 5, b: 2, c: 3, d: 444}).takeUntil((value, key, obj) => {
return value > 39;
}).toJS()); //{a: 5, b: 2, c: 3}複製程式碼
迴圈遍歷
map()
filter()
every()
some()
forEach()
reduce()
reduceRight()
。
// List
//1. map()
console.log(Immutable.fromJS([1, 2, 3, 4, 5]).map((value, index, array)=>{
return value * 2;
}).toJS()); // [2, 4, 6, 8, 10]
//2. filter()
console.log(Immutable.fromJS([1, 2, 3, 4, 5]).filter((value, index, array)=>{
return value % 2 === 0;
}).toJS()); // [2, 4]
// filterNot() ...這個沒有什麼卵用
//3. every()
console.log(Immutable.fromJS([1, 2, 3, 4, 5]).every((value, index, array)=>{
return value > 2;
})); // false
//4. some()
console.log(Immutable.fromJS([1, 2, 3, 4, 5]).some((value, index, array)=>{
return value > 2;
})); // true
//5. forEach() 返回迭代的條目數(包括返回false的最後一個迭代)
// 與Array 的 forEach不同,如果sideEffect的任何呼叫返回false,迭代將停止。 返回迭代的條目數(包括返回false的最後一個迭代)。
console.log(Immutable.fromJS([1, 2, 3, 4, 5, {a: 123}]).forEach((value, index, array)=>{
console.log(value, index, array.toJS(), 'forEach');
return value < 5;
})); // 5
//6. reduce()
// 同原生用法
//7. reduceRight()
// 同原生用法
// Map
//1. map()
console.log(Immutable.fromJS({a: 5, b: 2, c: 3, d: 444}).map((value, key, obj)=>{
return value * 2;
}).toJS()); // {a: 10, b: 4, c: 6, d: 888}
//2. filter()
console.log(Immutable.fromJS({a: 5, b: 2, c: 3, d: 444}).filter((value, key, obj)=>{
return value % 2 === 0;
}).toJS()); // {b: 2, d: 444}
// filterNot() ...這個沒有什麼卵用
//3. every()
console.log(Immutable.fromJS({a: 5, b: 2, c: 3, d: 444}).every((value, key, obj)=>{
return value > 2;
})); // false
//4. some()
console.log(Immutable.fromJS({a: 5, b: 2, c: 3, d: 444}).some((value, key, obj)=>{
return value > 2;
})); // true
//5. forEach() 返回迭代的條目數(包括返回false的最後一個迭代)
// 與Array 的 forEach不同,如果sideEffect的任何呼叫返回false,迭代將停止。 返回迭代的條目數(包括返回false的最後一個迭代)。
console.log(Immutable.fromJS({a: 5, b: 2, c: 3, d: 444}).forEach((value, key, obj)=>{
return value < 444;
})); // 4
//6. reduce()
// 同原List用法
//7. reduceRight()
// 同List用法複製程式碼
Map 特有 mapKeys() mapEntries()
對Map元素
進行處理,返回處理後的物件。
//mapKeys() 返回物件
console.log(Immutable.fromJS({a: 5, b: 2, c: 3, d: 444}).mapKeys((key)=>{
return key + 'hhh';
}).toJS());
// {ahhh: 5, bhhh: 2, chhh: 3, dhhh: 444}
//mapEntries() 返回物件
console.log(Immutable.fromJS({a: 5, b: 2, c: 3, d: 444}).mapEntries(([key, value])=>{
return [key + 'aaa', value+'hhhh'];
}).toJS());// {aaaa: "5hhhh", baaa: "2hhhh", caaa: "3hhhh", daaa: "444hhhh"}複製程式碼
merge
merge()
mergeDeep()
mergeWith()
mergeDeepWith()
// List
const $test = Immutable.fromJS([1, 2, 3, 7, {a: {b: 55, c: 66}}]);
const $test1 = Immutable.fromJS([1, 2, 3, 6, {a: {b: 333, d: 67}}]);
// 淺merge
console.log($test.merge($test1).toJS(), $test.toJS());
// $test1 -> $test [1, 2, 3, 6, {b: 333, d: 67}] [1, 2, 3, 7, {a: {b: 55, c: 66}}]
// 深merge
console.log($test.mergeDeep($test1).toJS(), $test.toJS());
// $test1 -> $test [1, 2, 3, 6, {b: 333, c: 66, d: 67}] [1, 2, 3, 7, {a: {b: 55, c: 66}}]
// 淺merge自定義merge規則
console.log($test.mergeWith((prev, next)=> {
// 自定義轉換
return prev;
}, $test1).toJS(), $test1.toJS());
// 深merge自定義merge規則
console.log($test.mergeDeepWith((prev, next)=> {
// 自定義轉換
return prev;
}, $test1).toJS(), $test1.toJS());
// Map
const $test = Immutable.fromJS({a: {a1: 222, a3: 456}, b: 2, c: 3, d: 444});
const $test1 = Immutable.fromJS({a: {a1: 222, a2: 234}, b: 2, c: 3, d: 444});
// 淺merge
console.log($test.merge($test1).toJS(), $test.toJS());
// $test1 -> $test {a: {a1: 222, a2: 234}, b: 2, c: 3, d: 444} {a: {a1: 222, a3: 456}, b: 2, c: 3, d: 444}
// 深merge
console.log($test.mergeDeep($test1).toJS(), $test.toJS());
// $test1 -> $test {a: {a1: 222, a2: 234, a3: 456}, b: 2, c: 3, d: 444} {a: {a1: 222, a3: 456}, b: 2, c: 3, d: 444}
// 淺merge自定義merge規則
console.log($test.mergeWith((prev, next)=> {
// 自定義轉換
return prev;
}, $test1).toJS(), $test1.toJS());
// 深merge自定義merge規則
console.log($test.mergeDeepWith((prev, next)=> {
// 自定義轉換
return prev;
}, $test1).toJS(), $test1.toJS());複製程式碼
jonin() 轉換為字串
使用方式和原生Array的joni()
一樣。
// List
console.log(Immutable.fromJS([1, 2, 3, {a: 123, b: 321}]).join()); // 1,2,3,Map { "a": 123, "b": 321 }
// Map
console.log(Immutable.fromJS({b: 2, a: {a1: 222, a3: 456}, c: 3, d: 444}).join()); // 2,Map { "a1": 222, "a3": 456 },3,444複製程式碼
isEmpty() 判空
// 判斷空List
console.log(Immutable.fromJS([]).isEmpty()); // true
// 判斷Map是否為空 比原生方便
console.log(Immutable.fromJS({}).isEmpty()); // true複製程式碼
has() hasIn() 檢查是否有某個key
// List
console.log(Immutable.fromJS([1, 2, 3, {a: 123, b: 321}]).has('0')); // true
console.log(Immutable.fromJS([1, 2, 3, {a: 123, b: 321}]).hasIn([3, 'b'])); // true
// Map
console.log(Immutable.fromJS({b: 2, a: {a1: 222, a3: 456}, c: 3, d: 444}).has('a')); // true
console.log(Immutable.fromJS({b: 2, a: {a1: 222, a3: 456}, c: 3, d: 444}).hasIn(['a', 'a3'])); // true複製程式碼
includes() 是否包含某些元素
includes()
、contains()
這倆等效。
// List
// 物件是否包含某個元素,對Immutable元素使用Immutable.is 進行比較
console.log(Immutable.fromJS([6, 5, 4, 3, 2, 1, 89]).includes('89'));// 陣列沒有字元89,所以返回 false
console.log(Immutable.fromJS([6, 5, 4, 3, 2, 1, '89']).contains('89'));// true
console.log(Immutable.fromJS([6, 5, 4, 3, 2, 1, Immutable.fromJS([6, 5, 4])]).contains(Immutable.fromJS([6, 5, 4])));// true
// Map
// 物件是否包含某個元素,對Immutable元素使用Immutable.is 進行比較
console.log(Immutable.fromJS({b: 2, a: {a1: 222, a3: 456}, c: 3, d: 89}).includes('89'));// 陣列沒有字元89,所以返回 false
console.log(Immutable.fromJS({b: 2, a: {a1: 222, a3: 456}, c: 3, d: '89'}).contains('89'));// true
console.log(Immutable.fromJS({b: 2, a: {a1: 222, a3: 456}, c: 3, d: Immutable.fromJS([6, 5, 4])}).contains(Immutable.fromJS([6, 5, 4])));// true複製程式碼
isSubset() 子集判斷
// List
// isSubset()
console.log(Immutable.fromJS([6, 5, 1, [6, 5, 4]]).isSubset(Immutable.fromJS([[6, 5, 4], 6, 5, 4, 3, 2, 1, '89'])));// true
// isSuperset 就是 isSubset 引數掉個個兒
console.log(Immutable.fromJS([[6, 5, 4], 6, 5, 4, 3, 2, 1, '89']).isSuperset(Immutable.fromJS([6, 5, 1, [6, 5, 4]])));// true
// Map
// isSubset()
console.log(Immutable.fromJS({b: 2, a: {a1: 222, a3: 456}}).isSubset(Immutable.fromJS({b: 2, a: {a1: 222, a3: 456}, c: 3, d: 5})));// true
// isSuperset 就是 isSubset 引數掉個個兒
console.log(Immutable.fromJS({b: 2, a: {a1: 222, a3: 456}, c: 3, d: 5}).isSuperset(Immutable.fromJS({b: 2, a: {a1: 222, a3: 456}})));// true複製程式碼
reverse() 反轉
// List
console.log(Immutable.fromJS([1, 2, 3, 4, 5, 6]).reverse().toJS());
// [6, 5, 4, 3, 2, 1]
// Map
console.log(Immutable.fromJS({b: 2, a: {a1: 222, a3: 456}, c: 3, d: 5}).reverse().toJS());
// {d: 5, c: 3, a: {a1: 222, a3: 456}, b: 2}複製程式碼
排序
sort()
和sortBy()
。
// List
// sort(comparator?: (valueA: V, valueB: V) => number): Iterable<K, V>
console.log(Immutable.fromJS([6, 5, 4, 3, 2, 1]).sort().toJS());
// 傳入比較函式
console.log(Immutable.fromJS([1, 2, 3, 4, 5, 6]).sort((a, b) => {
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
if (a === b) {
return 0;
}
}).toJS());
// sortBy
/*
sortBy<C>(
comparatorValueMapper: (value: T, key: number, iter: Iterable<number, T>) => C,
comparator?: (valueA: C, valueB: C) => number
): Iterable<number, T>
*/
console.log(Immutable.fromJS([{a: 1, b: {c: 22}}, {a: 2, b: {c: 22}}, {a: 1, b: {c: 22}},
{a: 3, b: {c: 22}}, {a: 10, b: {c: 22}}, {a: 9, b: {c: 22}}]).sortBy((value, index, array)=> {
return value.get('a')
},(a, b) => {
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
if (a === b) {
return 0;
}
}).toJS());
// Map
console.log(Immutable.fromJS({b: 2, a: 88, c: 3, d: 5}).sort().toJS());// {b: 2, c: 3, d: 5, a: 88}
// 傳入比較函式
console.log(Immutable.fromJS({b: 2, a: 88, c: 3, d: 5}).sort((a, b) => {
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
if (a === b) {
return 0;
}
}).toJS());// {b: 2, c: 3, d: 5, a: 88}
// sortBy
/*
sortBy<C>(
comparatorValueMapper: (value: T, key: number, iter: Iterable<number, T>) => C,
comparator?: (valueA: C, valueB: C) => number
): Iterable<number, T>
*/
console.log(Immutable.fromJS({b: {a: 2}, a: {a: 88}, c: {a: 3}, d: {a: 5}}).sortBy((value, key, obj)=> {
return value.get('a')
},(a, b) => {
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
if (a === b) {
return 0;
}
}).toJS());// {b: {a: 2}, c: {a: 3}, d: {a: 5}, a: {a: 88}}複製程式碼
flatten() 平鋪
引數預設情況下,false
深度平鋪,true
淺度平鋪1層。
// List
console.log(Immutable.fromJS([1, 2, 3, 4, [1, 11, 111, 12344], {a: 1234, b: {bb: [777, 888]}}, 5, 6]).flatten().toJS());
// [1, 2, 3, 4, 1, 11, 111, 12344, 1234, 777, 888, 5, 6]
console.log(Immutable.fromJS([1, 2, 3, 4, [1, 11, 111, 12344], {a: 1234, b: {bb: [777, 888]}}, 5, 6]).flatten(true).toJS());
// [1, 2, 3, 4, 1, 11, 111, 12344, 1234, Object, 5, 6]
// Map
console.log(Immutable.fromJS({b: 2, a: {a1: {a5: 333}, a3: [1,2,3]}, c: 3, d: 5}).flatten().toJS());
// {0: 1, 1: 2, 2: 3, b: 2, a5: 333, c: 3, d: 5}
console.log(Immutable.fromJS({b: 2, a: {a1: {a5: 333}, a3: [1,2,3]}, c: 3, d: 5}).flatten(true).toJS());
// {b: 2, a1: Object, a3: Array[3], c: 3, d: 5}複製程式碼
另外還有一個flatMap()
方法,它就等同於List([1,2,3,4,5,6]).map(...).flatten(true)
。
groupBy() 分組
返回值是OrderedMap
。
// List
console.log(Immutable.fromJS([{v: 0, a: 111}, {v: 1, a: {b: [1, 2, 3]}}, {v: 1, a: 333}, {v: 0, a: {b: [1, 2, 3]}}, {v: 1, a: 333}]).groupBy((value) => {
return value.get('a')
}).toJS());
// OrderedMap {111: Array[1], 333: Array[2], Map { "b": List [ 1, 2, 3 ] }: Array[2]}
// Map
console.log(Immutable.fromJS({b: {a5: 333}, a: {a5: 333}, c: {a5: 334}, d: {a5: 334}}).groupBy((value) => {
return value.get('a5')
}).toJS());
// OrderedMap {333: {b: {a5: 333}, a: {a5: 333}}, 334: {c: {a5: 334}, d: {a5: 334}}}複製程式碼
flip() Map 特有翻轉
console.log(Immutable.fromJS({b: 'b1', a: 'a1', c: 'c1', d: 'd1'}).flip().toJS()); // {b1: "b", a1: "a", c1: "c", d1: "d"}複製程式碼
連線 concat()
// List
const $test1 = Immutable.fromJS([1, 2, 3, 4, 5, 6]);
const $test2 = Immutable.fromJS([111, 222, 333, 444, 555, 666]);
console.log($test1.concat($test2).toJS()); //[1, 2, 3, 4, 5, 6, 111, 222, 333, 444, 555, 666]
console.log($test1.toJS(), $test2.toJS()); //[1, 2, 3, 4, 5, 6] [111, 222, 333, 444, 555, 666]
// Map
const $test1 = Immutable.fromJS({b: 2, a: {a1: {a5: 333}, a3: [1,2,3]}, c: 3, d: 5});
const $test2 = Immutable.fromJS({b1: 22, b: 34});
console.log($test1.concat($test2).toJS()); //{b: 34, a: Object, c: 3, d: 5, b1: 22} 屬性 b 被覆蓋
console.log($test1.toJS(), $test2.toJS()); //{b: 2, a: {a1: {a5: 333}, c: 3, d: 5} b1: 22, b: 34}複製程式碼
型別轉換
轉換為原生型別
// List
// 淺層
// toArray
console.log(Immutable.fromJS([1, 2, 3, 4, 5, 6, {a: {b: [1234, 22]}}]).toArray());// [1, 2, 3, 4, 5, 6, Map]
console.log(Immutable.fromJS([1, 2, 3, 4, 5, 6, [1234, 22]]).toArray());// [1, 2, 3, 4, 5, 6, List]
// toObject
console.log(Immutable.fromJS([1, 2, 3, 4, 5, 6, {a: {b: [1234, 22]}}]).toObject());// {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: Map}
console.log(Immutable.fromJS([1, 2, 3, 4, 5, 6, [1234, 22]]).toObject());// {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: List}
//深層
// 就是一直在用的 toJS(); 不到萬不得已,儘量不用。
// Map
// 淺層
// toArray
console.log(Immutable.fromJS({b: 2, a: {a1: {a5: 333}, a3: [1,2,3]}, c: 3, d: 5}).toArray());// [2, Map, 3, 5]
console.log(Immutable.fromJS({b: 2, a: [1, 2, 2], c: 3, d: 5}).toArray());// [2, List, 3, 5]
// toObject
console.log(Immutable.fromJS({b: 2, a: {a1: {a5: 333}, a3: [1,2,3]}, c: 3, d: 5}).toObject());// {b: 2, a: Map, c: 3, d: 5}
console.log(Immutable.fromJS({b: 2, a: [1, 2, 2]}).toObject());// {b: 2, a: List}
//深層
// 就是一直在用的 toJS(); 不到萬不得已,儘量不用。複製程式碼
轉換為其他ImmutableJS資料型別
暫時不做詳細介紹。
// toMap()
// toOrderedMap()
// toSet()
// toOrderedSet()
// toList()
// toStack()複製程式碼
效能調優,批處理
const $list1 = Immutable.List.of(1,2,3);
$list.push(4).push(5).push(6);
console.log($list1.toJS());// [1, 2, 3, 4, 5, 6]複製程式碼
在上面的程式碼中,進行三次push()
操作,會分別產生三個List
,其中頭兩次中間List
明顯是冗餘的,我們其實只關心最後一次產生的List
,這種情況就產生了冗餘操作,為了消除這種冗餘的操作,減少其中的效能損耗,我們可以先暫時把$list1
變成非immutable的,這樣我們進行push
操作時就不會產生冗餘的中間List
。
這裡面有效能比較:stack overflow 中的一個問題
。
在把Immutable型別的資料,轉為可變的之後,只有部分操作方法會返回可變的資料,其他操作方法仍然會返回immutable資料,但是這些方法應該就夠用了。
// Map: set(), merge()
// List: push(), pop(), shift(), unshift()複製程式碼
withMutations()
為此 withMutations()
函式把list
臨時變為可變的資料。
// List
// 將傳入的物件變成可變的物件,為了提高效能
const $list1 = Immutable.List.of(1,2,3);
const $list2 = $list1.withMutations(function ($list) {
$list.push(4).push(5).push(6);
// 為此 withMutations 函式把list臨時變為可變的資料,這三次push實質上只產生了一箇中間態list
});
console.log($list1.size);// 3
console.log($list2.size);// 6複製程式碼
// Map
const $obj1 = Immutable.fromJS({b: 2, a: [1, 2, 2]});
const $obj2 = $obj1.withMutations(function ($obj) {
$obj.set('c', 4444).set('d', 4444).set('e', 4444);
});
console.log($obj1.size);// 2
console.log($obj2.size);// 5複製程式碼
asMutable() asImmutable()
這兩個一定要配對使用.
// List demo:
const $test1 = Immutable.List.of(1,2,3);
const $test2 = $test1.asMutable(); // 變成可變物件
console.log($test1 === $test2, Immutable.is($test1, $test2)); // false true
const $test3 = $test2.set(0, 123); // 這裡沒有產生新的物件
console.log($test3.toJS(), $test2.toJS(), $test3 === $test2, Immutable.is($test3, $test2));// [123, 2, 3] [123, 2, 3] true true
// 變成不可變物件
const $test4 = $test3.asImmutable();
const $test5 = $test4.set(0, 234);
console.log($test4 === $test3, Immutable.is($test4, $test3));// true true
console.log($test4.toJS(), $test5.toJS(), $test4 === $test5, Immutable.is($test4, $test5));// [123, 2, 3] [234, 2, 3] false false複製程式碼
// Map demo:
const $test1 = Immutable.fromJS({b: 2, a: [1, 2, 2]});
const $test2 = $test1.asMutable(); // 變成可變物件
console.log($test1 === $test2, Immutable.is($test1, $test2)); // false true
const $test3 = $test2.set('b', 123); // 這裡沒有產生新的物件
console.log($test3.toJS(), $test2.toJS(), $test3 === $test2, Immutable.is($test3, $test2));// {b: 123, a: Array[3]} {b: 123, a: Array[3]} true true
// const $test3 = $test2.slice(1); // 這裡沒有產生新的物件
// console.log($test3.toJS(), $test2.toJS(), $test3 === $test2, Immutable.is($test3, $test2));// {a: Array[3]} {b: 2, a: Array[3]} false false
// 變成不可變物件
const $test4 = $test3.asImmutable();
const $test5 = $test4.set('b', 234);
console.log($test4 === $test3, Immutable.is($test4, $test3));// true true
console.log($test4.toJS(), $test5.toJS(), $test4 === $test5, Immutable.is($test4, $test5));// {b: 234, a: Array[3]} {b: 123, a: Array[3]} [234, 2, 3] false false複製程式碼
和React Redux 架構的結合
利用 immutable.js 不可變的特性,可以極大的優化React render的冗餘執行。React 官方提供的PureRenderMixin
是淺比較,具體的不用細說,網上一查一堆。
immutable-pure-render-decorator
專門針對immutable的PureRenderMixin
,用來裝飾React元件。
import {React} from 'base';
import pureRenderDecorator from '../../../widgets/libs/immutable-pure-render-decorator';
@pureRenderDecorator
export default class PartA extends React.Component {
constructor(props) {
super(props);
// 捨棄React.addons.PureRenderMixin
// this.shouldComponentUpdate = React.addons.PureRenderMixin.shouldComponentUpdate.bind(this);
}
render() {
console.log('元件PartA,render執行了');
const data = this.props.data;
return (
<section>
<div>
<p>我是元件PartA</p>
<p>{data.toJSON ? JSON.stringify(data.toJSON()) : data}</p>
</div>
</section>
)
}
}複製程式碼
優化shouldComponentUpdate()
我們都知道官方提供的React.addons.PureRenderMixin
提供的shouldComponentUpdate()
,只能進行淺比較,對於引用型別Object
、Array
比較無力,而如果使用Immutable的Map
和List
替換Object
、Array
,則可以使用Immutable.is()
來比較兩個引用型別,從而補充了React.addons.PureRenderMixin
的漏洞。
immutable-pure-render-decorator 原始碼
原始碼見附件
高階元件封裝
對於使用immutable.js的專案,在應用公共元件的時候,由於公共元件的內部實現一定是原生JS資料,所以我們只能傳遞原生JS資料到公共元件,但是如果轉換成了原生JS資料,就又會出現"React.addons.PureRenderMixin
提供的shouldComponentUpdate()
是淺比較"問題,對此可以使用下面的高階元件進行封裝。
/**
* Created by 百竿<shengdong.ysd@alibaba-inc.com> on 2017-2-6.
*/
import {React} from 'base';
// 通過Immutable.is 封裝過的 shouldComponentUpdate
import {shouldComponentUpdate} from '../immutable-pure-render-decorator';
export default ComposedComponent => {
return class extends React.Component {
constructor(props) {
super(props);
this.shouldComponentUpdate = shouldComponentUpdate.bind(this);
}
render() {
const props = this.props.toJS ? this.props.toJS() : this.props;
return <ComposedComponent {...this.props} {...props} />;
}
}
};
複製程式碼
demo:
import {React} from 'base';
import { connect } from 'react-redux';
import highComponent from '../../../../widgets/libs/utils/highComponent';
import actions from '../../actions';
// 公共元件
import Dialog from '@alife/dialog';
// import Immutable from 'immutable';
function mapStateToProps(state) {
return {
open: state.getIn(['dialog', 'open']),
title: state.getIn(['dialog', 'title'])
}
}
function mapDispatchToProps(dispatch) {
return {
onPrimaryTouchTap: ()=> {
dispatch(actions.toggleDialog(false));
},
onSecondaryTouchTap: ()=> {
dispatch(actions.toggleDialog(false));
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(highComponent(Dialog))//通過高階元件封裝複製程式碼