[js] immutable 实例
原生js
js中的对象
let a = {};
let b = {};
console.log(a == b);//false
console.log(a === b);//false
console.log({} == {});//false
console.log({} === {});//false
let c = { a: 'a', b: 'b' };
let d = { a: 'a', b: 'b' };
console.log(c == d);//false
console.log(c === d);//false
console.log({ a: 'a', b: 'b' } == { a: 'a', b: 'b' });//false
console.log({ a: 'a', b: 'b' } === { a: 'a', b: 'b' });//false
let e = { a: 'a', b: 'b' };
let f = e;
console.log(e == f);//true
console.log(e === f);//true
e.a = 'aa';
console.log(e == f);//true
console.log(e === f);//true
js中的数组
let a = [];
let b = [];
console.log(a == b);//false
console.log(a === b);//false
console.log([] == []);//false
console.log([] === []);//false
let c = ['a', 'b'];
let d = ['a', 'b'];
console.log(c == d);//false
console.log(c === d);//false
console.log(['a', 'b'] == ['a', 'b']);//false
console.log(['a', 'b'] === ['a', 'b']);//false
let e = ['a', 'b'];
let f = e;
console.log(e == f);//true
console.log(e === f);//true
e[0] = 'aa';
console.log(e == f);//true
console.log(e === f);//true
它们的问题在于无法比较对象/数组是否发生了变化。
immutable 与 Object.freeze、const 区别
Object.freeze 和 const 都可以达到防止对象被篡改的功能,但它们是 shallowCopy 的。对象层级一深就要特殊处理了。
is 、 equals
比较两个数据是否相等。
let a = fromJS({ a: 1, b: 2 });
let b = fromJS({ a: 1, b: 2 });
console.info(a.equals(b))//true
console.info(is(a, b))//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
size 、count
获得数据的length。
不同于js,immutable里对象也可以获得length。
let a = fromJS({ a: 1, b: 2 });
let b = fromJS({ a: 1, b: 2, c: 3 });
console.info(a.size)//2
console.info(b.count())//3
has
{
let a = fromJS({ a: 1, b: 2, c: '', d: undefined });
console.log(a.has('a'));//true
console.log(a.has('c'));//true
console.log(a.has('d'));//true
console.log(a.has('e'));//false
let b = fromJS(['a', 'b', 'c']);
console.log(b.has(1));//true
console.log(b.has(4));//false
}
find
let a = fromJS({ a: 1, b: 2, c: { d: 3, e: 4 } });
let b = a.find((v) => {
//return v == fromJS({ d: 3, e: 4 });//error
//return v.equals(fromJS({ d: 3, e: 4 }));//error
//return fromJS({ d: 3, e: 4 }).equals(v);//ok
return is(v, fromJS({ d: 3, e: 4 }));//ok
})
console.info(b.toJS())//{ d: 3, e: 4 }
keySeq 、valueSeq
let a = fromJS({ a: 1, b: 2, c: { d: 3, e: 4 } });
console.info(a.keySeq().toJS())//["a", "b", "c"]
console.info(a.valueSeq().toJS())//[1, 2, { d: 3, e: 4 }]
forEach 、 map
不同于js,immutable里对象也可以用forEach 、 map进行遍历。
forEach(不同于js的forEach,返回 false 会终止)。
除了forEach 、 map,还可以使用filter 、reduce 等方法,具体看文档。
{
let a = fromJS(['a', 'b', 'c']);
let b = a.forEach((v) => {
console.log(v)
})
//a
//b
//c
console.log(b)//3
let c = a.map((v) => {
console.log(v)
return v + '1';
})
//a
//b
//c
console.log(c.toJS())//["a1", "b1", "c1"]
}
{
let a = fromJS({ a: 'a2', b: 'b2', c: 'c2' });
let b = a.forEach((v) => {
console.log(v)
})
//a2
//b2
//c2
console.log(b)//3
let c = a.map((v) => {
console.log(v)
return v + '3';
})
//a2
//b2
//c2
console.log(c.toJS())//{a: "a23", b: "b23", c: "c23"}
}
get 、set 、update 、delete 、getIn 、setIn 、updateIn 、deleleIn
{
let a = fromJS({ a: 1, b: 2, c: '', d: undefined });
console.log(a.get('a'));//1
console.log(a.get('c'));//''
console.log(a.get('d'));//undefined
console.log(a.get('e'));//undefined
let b = fromJS(['a', 'b', 'c']);
console.log(b.get(1));//b
console.log(b.get(4));//undefined
//console.log(b.get(0).toJS());//error 一旦获取到非对象/数组数据,便不再是immutable结构
let c = fromJS({ a: 1, b: { bb: 2, cc: 22 }, c: 3 });
console.log(c.getIn(['b', 'bb']));//2
console.log(c.getIn(['b', 'dd']));//undefined
let d = c.setIn(['b', 'bb'], 4)
console.log(d.getIn(['b', 'bb']));//4
let e = c.updateIn(['b', 'bb'], (v) => {
return 44;
})
console.log(e.getIn(['b', 'bb']));//44
let f = fromJS({ a: 1, b: { bb: { x: { x2: 'x2', x3: 'x3' }, y: 'y1' }, cc: 22 }, c: 3 });
let g = f.updateIn(['b', 'bb', 'x'], (v) => {
console.info(v.toJS());//{x2: "x2", x3: "x3"}
return v.set('x3', 'xyz');
})
console.info(g.getIn(['b', 'bb', 'x']).toJS());//{x2: "x2", x3: "xyz"}
let h = f.updateIn(['b', 'bb', 'x'], (v) => {
//return v.delete(1);//error 对象用索引不行
return v.delete('x3');
})
console.info(h.getIn(['b', 'bb', 'x']).toJS())//{x2: "x2"}
let i = f.deleteIn(['b', 'bb', 'x', 'x3']);
console.info(i.getIn(['b', 'bb', 'x']).toJS())//{x2: "x2"}
}
{
//可以创建新的键,但是不可以创建新的对象/数组
let a = fromJS({ a: 1, b: { bb: 2, cc: 22 }, c: 3 });
console.info(a.set('x', 4).toJS())//{ a: 1, b: { bb: 2, cc: 22 }, c: 3 ,x:4}
console.info(a.update('y', (v) => { return 5 }).toJS())//{ a: 1, b: { bb: 2, cc: 22 }, c: 3 ,y:5}
//a.setIn(['a', 'bb'], 4)//error
//a.updateIn(['a', 'bb'], (v) => { return 4 })//error
console.info(a.setIn(['b', 'bb'], 4).toJS())//{ a: 1, b: { bb: 4, cc: 22 }, c: 3 }
console.info(a.setIn(['b', 'dd'], 4).toJS())//{ a: 1, b: { bb: 2, cc: 22,dd:4 }, c: 3 }
}
getIn对对象使用时,([])里的参数应该为string类型.
let a = fromJS({ 1: 'a', 2: 'b', 3: { 4: 'c', 5: 'd' } });
console.log(a.getIn([3, 4]))//undefined
console.log(a.getIn(['3', '4']))//c
delete
delete(index: number)
删除 index 位置的元素
超过范围/不存在的健,不会强制报错
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}
get getIn
只有数组可以用 number 类型 的key
get(key: number, notSetValue?: T)
// notSetValue 默认值
console.log($test.get(11, 'no have value')); // no have value
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
list结构
React 组件 propTypes 判断是否是 List:
React.PropTypes.instanceOf(Immutable.List).isRequired
获取 List 索引的元素(负数也是能运行的):
immutableData.get(0)
immutableData.get(-1) #反向索引
console.info(fromJS(['x', 'y', 'z']).get(0));//x
通过 getIn 访问嵌套数组当中的数据:
console.info(fromJS([['x', 'xx'], 'y', 'z']).getIn([0, 1]));//xx
fromJS 与 List 、 Map的区别
fromJS可进行嵌套操作,而List 、 Map 不可以。
let map = Map({ a: { aa: 'aa', bb: 'bb' }, b: 'b' });
let list = List([['a', 'aa'], 'b']);
//let map1 = map.getIn(['a', 'bb']);//error
//console.log(map1)//error
//let list1 = list.getIn([0, 1]);//error
//console.log(list1)//error
//let map2 = map.setIn(['a', 'bb'], 'xx');//error
//console.log(map2.toJS())//error
//let list2 = list.getIn([0, 1], 'yy');//error
//console.log(list2.toJS())//error
console.log(map.toJS())
console.log(list.toJS())
合并数据
merge
注意merge的参数应为较新的数据。
对象和数组都是按照key来merge的。
- 相同结构的merge
let map1 = fromJS({ a: 1, b: 2, c: 3 });
let map2 = map1.set('b', 50);
let map3 = map1.merge(map2);
let map4 = map2.merge(map1);
console.log(map1.get('b')); // 2
console.log(map2.get('b')); // 50
console.log(map1.toJS());//{a: 1, b: 2, c: 3}
console.log(map2.toJS());//{a: 1, b: 50, c: 3}
console.log(map3.toJS());//{a: 1, b: 50, c: 3}
console.log(map4.toJS());//{a: 1, b: 2, c: 3}
let list1 = fromJS(['a', 'b', 'c']);
let list2 = list1.set(1, 'bb');
let list3 = list1.merge(list2);
let list4 = list2.merge(list1);
console.log(list1.get(1)); // b
console.log(list2.get(1)); // bb
console.log(list1.toJS());//["a", "b", "c"]
console.log(list2.toJS());//["a", "bb", "c"]
console.log(list3.toJS());//["a", "bb", "c"]
console.log(list4.toJS());//["a", "b", "c"]
- 相同结构的merge
let map1 = fromJS({ a: 1, b: 2 });
let map2 = fromJS({ a: 1, c: 3 });
let map3 = map1.merge(map2);
let map4 = map2.merge(map1);
console.log(map1.toJS());//{a: 1, b: 2}
console.log(map2.toJS());//{ a: 1, c: 3 }
console.log(map3.toJS());//{a: 1, b: 2, c: 3}
console.log(map4.toJS());//{a: 1, c: 3, b: 2}
let list1 = fromJS(['a', 'b', 'c']);
let list2 = fromJS(['a', 'bb']);
let list3 = list1.merge(list2);
let list4 = list2.merge(list1);
console.info(list1.toJS());//["a", "b", "c"]
console.log(list2.toJS());//['a', 'bb']
console.log(list3.toJS());//["a", "bb", "c"]
console.log(list4.toJS());//["a", "b", "c"]
react中的使用
getInitialState() {
return {
data: Map({ times: 0 })
}
},
handleAdd() {
this.setState({ data: this.state.data.update('times', v => v + 1) });
// 这时的 times 并不会改变
console.log(this.state.data.get('times'));
}
//可以简写成:
handleAdd() {
this.setState(({data}) => ({
data: data.update('times', v => v + 1) })
});
}
数据类型
- 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)。