Immutable

Immutable

Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。Immutable 实现的原理是 Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。

官网地址:https://github.com/immutable-js/immutable-js

深拷贝与浅拷贝的关系

(1) var arr = { } ; arr2 = arr ;

(2) Object.assign() 只是一级属性复制,比浅拷贝多拷贝了一层而已。

(3) const obj1 = JSON.parse(JSON.stringify(obj)); 数组,对象都好用的方法(缺点: 不能有undefined)

Immutable性能优化方式

Immutable 实现的原理是 Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了 Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。

Immutable常用数据类型

1.Map

1
2
3
4
const { Map } = require('immutable'); 
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = map1.set('b', 50);
map1.get('b') + " vs. " + map2.get('b'); // 2 vs. 50

简单应用场景:

1
2
3
4
5
6
7
8
9
import {Map} from 'immutable'
let a = Map({
select:'users',
filter:Map({name:'sola'})
})
let b = a.set('select','people')

a === b // false
a.get('filter') === b.get('filter') // true

如果上述select属性给一个组件用,因此值改变了,shouldComponentUpdate应该返回true,而filter属性给另一个组件使用,通过判断并无变化,shouldComponentUpdate应该返回false。此组件就避免了重复进行diff对比

2.List

1
2
3
4
5
6
7
8
9
10
11
const { List } = require('immutable'); 
const list1 = List([ 1, 2 ]);
const list2 = list1.push(3, 4, 5);
const list3 = list2.unshift(0);
const list4 = list1.concat(list2, list3);
assert.equal(list1.size, 2);
assert.equal(list2.size, 5);
assert.equal(list3.size, 6);
assert.equal(list4.size, 13);
assert.equal(list4.get(0), 1);
//push, set, unshift or splice 都可以直接用,返回一个新的immutable对象

3.merge,concat

1
2
3
4
5
6
7
8
9
10
11
const { Map, List } = require('immutable'); 
const map1 = Map({ a: 1, b: 2, c: 3, d: 4 });
const map2 = Map({ c: 10, a: 20, t: 30 });
const obj = { d: 100, o: 200, g: 300 };
const map3 = map1.merge(map2, obj);
// Map { a: 20, b: 2, c: 10, d: 100, t: 30, o: 200, g: 300 }
const list1 = List([ 1, 2, 3 ]);
const list2 = List([ 4, 5, 6 ]);
const array = [ 7, 8, 9 ];
const list3 = list1.concat(list2, array);
// List [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

4.toJS

将immutable转成普通JS对象

1
2
3
4
5
6
const { Map, List } = require('immutable');
const deep = Map({ a: 1, b: 2, c: List([ 3, 4, 5 ]) });
console.log(deep.toObject()); // { a: 1, b: 2, c: List [ 3, 4, 5 ] }
console.log(deep.toArray()); // [ 1, 2, List [ 3, 4, 5 ] ]
console.log(deep.toJS()); // { a: 1, b: 2, c: [ 3, 4, 5 ] }
JSON.stringify(deep); // '{"a":1,"b":2,"c":[3,4,5]}'

5.fromJS

将普通JS对象转为immutable对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const { fromJS } = require('immutable'); 
const nested = fromJS({ a: { b: { c: [ 3, 4, 5 ] } } });
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ] } } }
const nested2 = nested.mergeDeep({ a: { b: { d: 6 } } });
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 6 } } }
console.log(nested2.getIn([ 'a', 'b', 'd' ])); // 6
//如果取一级属性 直接通过get方法,如果取多级属性 getIn(["a","b","c"]]) //setIn 设置新的值
const nested3 = nested2.setIn([ 'a', 'b', 'd' ], "kerwin");
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: "kerwin" } } }
//updateIn 回调函数更新
const nested3 = nested2.updateIn([ 'a', 'b', 'd' ], value => value + 1);
console.log(nested3);
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 7 } } }
const nested4 = nested3.updateIn([ 'a', 'b', 'c' ], list => list.push(6));
// Map { a: Map { b: Map { c: List [ 3, 4, 5, 6 ], d: 7 } } }

redux Immutable应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//reducer.js 
const initialState = fromJS({category: "", material: ""})
const reducer = (prevstate = initialState, action = {}) => {
let {type, payload} = action
switch (type) {
case GET_HOME:
var newstate = prevstate.set("category", fromJS(payload.category))
var newstate2 = newstate.set("material", fromJS(payload.material))
return newstate2;
default:
return prevstate
}
}
//home.js
const mapStateToProps = (state) => {
return {
category: state.homeReducer.getIn(["category"]) || Map({}),
material: state.homeReducer.getIn(["material"]) || Map({})
}
}
this.props.category.get("相关属性")
this.props.category.toJS() //或者转成普通对象

Immutable也存在一定的缺点:

  • 容易跟原生JS混淆
  • 文档与调试不方便