CharmyZ/React

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映射以及向量尝试,和ClojureScala中流行的那些一样,最小化需要拷贝和缓存的数据。

Immutable.js也提供了惰性Seq, 允许有效的方法链式操作,例如mapfilter,不用创建中介变量。使用RangeRepeat创建一些Seq

Immutable.js 的 API 主要包含以下几部分:

  • formJS()

    JavaScript ObjectArray 彻底转换为 Immutable MapList
  • 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 函数的情况下,默认将原生JSArray转为ListObject转为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;
}

通过源码可以发现其默认只转换ObjectArray,其他原生类型原封不动返回。

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/valueobject,在 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 比较的是两个对象的 hashCodevalueOf(对于 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() {
    return 
foo
; } }

immutable-pure-render-decorator

专门针对immutablePureRenderMixin,用来装饰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]

poppushshiftunshift

List 数据类型也拥有 poppushshiftunshift 这四种操作方法,和原生 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() 转换为字符串

使用方式和原生Arrayjoni() 一样。

// 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