ImmutableJS 入门
Opened this issue · 0 comments
JavaScript
中的对象一般是可变的(Mutable),因为使用了引用赋值,新的对象简单的引用了原始对象,改变新的对象将影响到原始对象。例:
var obj1 = { a: 1 }; var obj2= obj1; obj2.a = 2; console.log(obj1.a); //=> 2
通常一般做法是使用 deepCopy
(深拷贝)来避免修改,但这样会产生较多的资源浪费。为了很好的解决这个问题,就可以使用 Immutable.js
。
Immutable.js
所创建的数据有一个特性:数据创建后不会被改变。
不可变数据一旦创建就不能改变,这样可简化应用开发、无防御复制、启用更先进的内存方案,以及使用更简单的逻辑检查更新。持久化数据提供可修改的API,这些API不在原地更新数据,而是产生新的更新后的数据。
Immutable.js
相比深拷贝的优势在于区分发生变化的数据和未变化的数据。例:
import Immutable from 'immutable'; var obj1 = Immutable.Map({ a: 1, b: 2 }); var obj2= obj1.set('a', 3); obj1.get('a'); //=> 1 obj2.get('a'); // =>3
对于上面的 obj1 和 obj2,a 是变化的数据,所以 obj1 和 obj2 各保存一份 a 数据,而 b 是未变化的数据,所以 obj1 和 obj2 共享 b 的数据。
Immutable.js
可以容易地共享没有被修改的资料。
Immutable.js
提供了很多持久化不可变数据结构,包括: List
, Stack
, Map
, OrderedMap
, Set
, OrderedSet
以及Record
。
这些数据结构在现代JavaScript
虚拟机都非常高效的,使用的是通过hash
映射以及向量尝试,和Clojure
和Scala
中流行的那些一样,最小化需要拷贝和缓存的数据。
Immutable.js
也提供了惰性Seq
, 允许有效的方法链式操作,例如map
和filter
,不用创建中介变量。使用Range
和Repeat
创建一些Seq
。
Immutable.js
的 API 主要包含以下几部分:
-
formJS()
将JavaScript Object
和Array
彻底转换为Immutable Map
和List
。 -
is()
与Object.is()
类似都是对值的比较,但它会将Immutable Iterable
视为值类型数据而不是引用类型数据,如果两个Immutable Iterable
的值相等,则返回true
。与Object.is()
不同的是,is(0, -0)
的结果为true
。 -
List
有序索引集,类似于JavaScript
中的Array
。 -
Map
无序Iterable
,读写Key
的复杂度为O(log32 N)
。 -
OrderedMap
有序Map
,排序依据是数据的set()
操作。 -
Set
元素为独一无二的集合,添加数据和判断数据是否存在的复杂度为O(log32 N)
。 -
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
用于衍生新的Record
类,进而生成Record
实例。Record
实例类似于JavaScript
中的Object
实例,但只接收特定的字符串作为key
,且拥有默认值。 -
Seq
序列(may not be backed by a concrete data structure)。 -
Iterable
可以被迭代的(Key, Value)
键值对集合,是Immutable.js
中其他所有集合的基类,为其他所有集合提供了 基础的Iterable
操作函数(比如map()
和filter
)。 -
Collection
创建Immutable
数据结构的最基础的抽象类,不能直接构造该类型。
Immutable.js
的 主要API 举例:
1. fromJS()
fromJS()
是最常用的将原生JS数据转换为 ImmutableJS
数据的转换方法。使用方式类似于 JSON.parse()
,接收两个参数:json
数据和 reviver
函数。
在不传递 reviver
函数的情况下,默认将原生JS
的Array
转为List
,Object
转为Map
.
// 常用 const t1 = Immutable.fromJS({a: {b: [1, 2, 3]}, c: 4}); console.log(t1); // 不常用 const t2 = Immutable.fromJS({a: {b: [1, 2, 3]}, c: 4}, 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
,其他原生类型原封不动返回。
2.List
List()
是一个构造方法,可以用于创建新的 List
数据类型,上面代码演示了该构造方法接收的参数类型,此外 List
拥有两个静态方法:
List.isList(value)
,判断value
是否是List
类型List.of(...values)
,创建包含...values
的列表
List
数据类型,对应原生 Array
数组。和原生数组,最大区别不存在'空位'。[, , , , ]
List
有序且可以重复值,对应于一般的 Array
例:
const List= Immutable.List; // 1. 取得 List 长度 const arr1 = List([1, 2, 3]); arr1.size // => 3 // 2. 新增或取代 List 元素内容 // set(index: number, value: T) // 将 index 位置的元素替换 const arr2 = arr1.set(-1, 7); // => [1, 2, 7] 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, 2); // => [1, 2, 2, 3] // 5. 清空 List // clear() const arr6 = arr1.clear(); // => []
3.Map
Map
:类似于 key/value
的 object
,在 ES6
也有原生 Map
对应
Map
循环时无序(orderedMap
有序),可以使用任何类型的数据作为 Key
值,并使用 Immutable.is()
方法来比较两个 Key
值是否相等:
console.log(Map().set(List.of(1), 'list-of-one').get(List.of(1)));// => 'list-of-one' console.log(Map().set(NaN, 'NaN').get(NaN)); console.log(Map().set(undefined, 'undefined').get(undefined)); console.log(Map().set(null, 'null').get(null));
Map()
是 Map 类型的构造方法,行为类似于 List()
,用于创建新的 Map
实例,此外,还包含两个静态方法:Map.isMap()
和 Map.of()
。
const Map= Immutable.Map; // 1. Map 大小 const map1 = Map({ a: 1 }); map1.size // => 1 // 2. 新增或取代 Map 元素 // set(key: K, value: V) const map2 = map1.set('a', 7); // => Map { "a": 7 } // 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', () => (7)) // => Map { "a": 7 } // 6. 合并 Map const map6 = Map({ b: 3 }); map1.merge(map6); // => Map { "a": 1, "b": 3 }
简单介绍
OrderedMap
OrderedMap
是Map
的变体,它除了具有Map
的特性外,还具有顺序性,当开发者遍历OrderedMap
的实例时,遍历顺序为该实例中元素的声明、添加顺序。OrderedMap
比非有序Map
更昂贵,并且可能消耗更多的内存。如果真要求遍历有序,请使用List
。
4.Set
Set
和 ES6
中的 Set
类似,都是没有重复值的集合,OrderedSet
是 Set
的遍历,可以保证遍历的顺序性。
const Set= Immutable.Set; // 1. 建立 Set const set1 = Set([1, 2, 3]); // => Set { 1, 2, 3 } // 2. 新增元素 const set2 = set1.add(1).add(5); // => Set { 1, 2, 3, 5 } // 由于 Set 为不能重复集合,故 1 只能出现一次 // 3. 删除元素 const set3 = set1.delete(3); // => Set { 1, 2 } // 4. 取联集 const set4 = Set([2, 3, 4, 5, 6]); set1.union(set4); // => Set { 1, 2, 3, 4, 5, 6 } // 5. 取交集 set1.intersect(set4); // => Set { 2, 3 } // 6. 取差集 set1.subtract(set4); // => Set { 1 }
5.is()
immutable
数据应该被当作值而不是对象,值是表示该事件在特定时刻的状态。这个原则对理解不可变数据的适当使用是最重要的。为了将Immutable.js
数据视为值,就必须使用Immutable.is()
函数或.equals()
方法来确定值相等,而不是确定对象引用标识的===
操作符。
两个immutable
对象可以使用===
来比较,这样是直接比较内存地址,性能最好。但即使两个对象的值是一样的,也会返回false
:
let map1 = Immutable.Map({a:1, b:1, c:1}); let map2 = Immutable.Map({a:1, b:1, c:1}); map1 === map2; // false
is()
就是用来对两个immutable
对象进行值比较的。使用方式类似于 Object.is(obj1, obj2)
,接收两个参数。
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
is()源码:
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; }
Immutable.is
比较的是两个对象的 hashCode
或 valueOf
(对于 JavaScript
对象)。由于 immutable
内部使用了 Trie
数据结构来存储,只要两个对象的 hashCode
相等,值就是一样的。这样的算法避免了深度遍历比较,性能非常好。
6.Stack
Stack
是基于 Signle-Linked List
实现的可索引集合,使用 unshift(v)
和 shift()
执行添加和删除元素的复杂度为 O(1)
。
// 1. 创建 Stack 实例 const $stack1 = Stack([1, 2, 3]); // => Stack [ 1, 2, 3 ] // 2. 取第一个元素 $stack1.peek() // => 1 // 3. 取任意位置元素 $stack1.get(2) // => 3 // 4. 判断是否存在 $stack1.has(10) // => false
7. Range() 和 Repeat()
Range(start?, end?, step?)
接收三个可选参数,使用方法如下:
// 1. 不传参 Range(); // => Range [ 0...Infinity ] // 2. 设置 start 起点 Range(10); // => Range [ 10...Infinity ] // 3. 设置 start 起点和 end 终点 Range(10, 20); // => Range [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ] // 4. 设置 start 起点、end 终点和 step 步长 Range(10, 20, 3); // => Range [ 10, 13, 16, 19 ]
Repeat(value, times?)
接收两个参数,其中 times
重复次数是可选参数:
Repeat('foo'); // => Repeat [ foo Infinity times ] Repeat('foo', 3); // => Repeat [ foo 3 times ]
类似 Range()
和 Repeat(value)
这样生成无限长度集合的操作,内部都存在惰性计算的机制,只有真实取值时才会生成相应的结果。使用 ES6
中的 Generator
函数,可以轻松实现一个惰性计算:
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 }
8. Record
Record
在表现上类似于 ES6
中的 Class
,但在某些细节上还有所不同。通过 Record()
可以创建一个新的 Record
类,使用该类可以创建具体的 Record
实例,该实例包含在 Record()
构造函数中声明的所有属性和默认值。如果 Record
实例中的某个属性被删除了,则只会讲实例中的属性值恢复为默认值:
// 1. 创建 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() // => 4
9. Seq
Seq
有两个特点:immutable
,一旦创建就不能被修改;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
的元素,则执行过程为:
console.log(oddSquares.get(1)); // filter(1) // filter(2) // filter(3) // map(3) // => 9
Seq()
是 Seq
的构造方法,它根据传入的参数类型,输出响应的 Seq
类型:
- 输入
Seq
,输出Seq
Iterable
=> 同类型的Seq(Keyed, Indexed, Set)
Array-like
=>Seq.Indexed
- 附加
Iterator
的Object
=>Seq.Indexed
Iterator
=>Seq.indexed
Object
=>Seq.Keyed
默认情况下Seq
的惰性计算结果不会被缓存,比如在下面的代码中,由于每个join()
都会遍历执行map
,所以map
总共执行了六次:
var squares = Seq.of(1,2,3).map(x => x * x); squares.join() + squares.join();
如果开发者知道 Seq
的结果会被反复用到,那么就可以使用 cacheResult()
将惰性计算的结果保存到内存中:
var squares = Seq.of(1,2,3).map(x => x * x).cacheResult(); squares.join() + squares.join();
10. Iterable 和 Collection
Iterable
是键值对形式的集合,其实例可以执行遍历操作,是immutable.js
中其他数据类型的基类,所有扩展自 Iterable
的数据类型都可以使用 Iterable
所声明的方法,比如 map
和 filter
等。
Collection
是 Concrete Data Structure
的基类,使用该类时需要至少继承其子类中的一个:Collection.Keyed / Collection.Indexed / Collection.Set。
更多认识
Cursor
的概念
这个 Cursor
和数据库中的游标是完全不同的概念。
由于 Immutable
数据一般嵌套非常深,为了便于访问深层数据,Cursor
提供了可以直接访问这个深层数据的引用。
import Immutable from 'immutable'; import Cursor from 'immutable/contrib/cursor'; let data = Immutable.fromJS({ a: { b: { c: 1 } } }); // 让 cursor 指向 { c: 1 } let cursor = Cursor.from(data, ['a', 'b'], newData => { // 当 cursor 或其子 cursor 执行 update 时调用 console.log(newData); }); cursor.get('c'); // 1 cursor = cursor.update('c', x => x + 1); cursor.get('c'); // 2
React 效能优化
ImmutableJS
除了可以和Flux/Redux
整合外,也可以用于基本 react
效能优化。
在 React 官方文档的**《Advanced Performance》** 一节中,专门对 React
的性能瓶颈、优化方式做了详细的解析。当一个 React 组件的 props
和 state
发生变化时,React
会根据变化后的 props
和 state
创建一个新的 virtual DOM
,然后比较新旧两个 vritual DOM
是否一致,只有当两者不同时,React
才会将 virtual DOM
渲染真实的 DOM
结点,而对 React
进行性能优化的核心就是减少渲染真实 DOM
结点的频率,间接地指出开发者应该准确判断 props
和 state
是否真正发生了变化。
在比对新旧 vritual DOM
和渲染真实 DOM
前,React
为开发者提供了 shouldComponentUpdate()
方法中断接下来的比对和渲染操作,默认情况下,该方法总会返回 true
,如果它返回 false
,则不执行比对和渲染操作:
// 最简单的实现: shouldComponentUpdate (nextProps) { return this.props.value !== nextProps.value; }
看起来挺简单,实在不然。当我们需要比对的值是对象、数组等引用值时,就会出现问题:
// 假设 this.props.value 是 { foo: 'bar' } // 假设 nextProps.value 是 { foo: 'bar' }, // 显然这两者引用的内存地址不同,但它们具有相同的值,这种时候不应该继续执行渲染 this.props.value !== nextProps.value; // true
如果数据是 Immutable Data
的话,那么数据发生变化就会生成新的对象,开发者只需要检查对象应用是否发生变化即可:
var SomeRecord = Immutable.Record({ foo: null }); var x = new SomeRecord({ foo: 'bar' }); var y = x.set('foo', 'baz'); x === y; // false
处理这一问题的另一种方式是通过 setter
设置 flag
对脏数据进行检查,但冗杂的代码是在让人头疼。
在 ES6
中可以使用官方文件上的 PureRenderMixin
进行比较,可以让程式码更简洁:
import PureRenderMixin from 'react-addons-pure-render-mixin'; class FooComponent extends React.Component { constructor(props) { super(props); this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); } render() { returnfoo; } }
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 () } } 我是组件PartA
{data.toJSON ? JSON.stringify(data.toJSON()) : data}
更多示例
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的有1个
// 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 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( comparatorValueMapper: (value: T, key: number, iter: Iterable) => C, comparator?: (valueA: C, valueB: C) => number ): Iterable */ 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( comparatorValueMapper: (value: T, key: number, iter: Iterable) => C, comparator?: (valueA: C, valueB: C) => number ): Iterable */ 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