Immutable.js 简介
Immutable.js是由 Facebook 工程师( Lee Byron) 花费 3 年时间打造,与 React 同期出现,虽然没有被默认放到 React工具集里,但是它的价值还是非常大的。它内部实现了一套完整的持久化数据结构( Persistent Data Structure ),还有很多易用的数据类型(像 List、Map)。有非常全面的函数式操作方法( 像map、get、getIn、set、setIn、merge)。同时 API 也尽量与原生Js的 Object 和 Array 类似。
Immutable.js 原理
Immutable Data
- Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改、添加或删除操作都会返回一个新的 Immutable 对象。
- Immutable 实现原理是持久化数据结构(Persistent Data Structure),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。
- 为了避免深度拷贝(deep Copy)把所有节点都复制一遍带来的性能损耗,Immutable 使用了共享结构(Structural Sharing),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。
用一张图来说明
为什么要用Immutable?
这个就说来话长,直接上代码,从问题说起。(全文拿Object类型举例)
// 首先字符串声明和赋值,
let str1 = '123';
let str2 = str1;
str2 = '456';
console.log(str1); // '123'
console.log(str2); // '456'
// 没有什么问题
// 对象的声明 和 赋值
let obj1 = { a : 1, b : 2 };
let obj2 = obj1; // 引用赋值
obj2.b = 5;
console.log(obj1.b); // 5
console.log(obj2.b); // 5
// 改变obj2中b的值,obj1中的b也会跟着变
这就是引用赋值在搞鬼,什么是引用赋值呢,拿上边的那个举例。obj1和obj2指向了js内存中的同一个对象,也就是指向同一个地址,共用一个源数据,所以,改变其中任何一个,其他引用的对象都会改变。
这就不得不说一下js的数据类型,引用类型的数据都是引用赋值。
- 基本类型 : String、 Number、 Boolean、Undefined、 Null
- 引用类型 : Object、 Array、 Function
js中也有解决引用赋值问题的方法
- 浅拷贝
let obj1 = {
a: 1,
b: 2,
c: {
d: 3,
e: 4
}
};
let obj2 = Object.assign({}, obj1);
obj2.b = 5;
obj2.c.d = 6;
console.log(obj1.b); // 2
console.log(obj2.b); // 5
console.log(obj1.c.d); // 6
console.log(obj2.c.d); // 6
这段代码可以看出来,浅拷贝只能保证数据第一层的数据不会跟着变,嵌套的引用类型,还是会出现引用赋值的现象
- 深拷贝
let obj1 = {
a: 1,
b: 2,
c: {
d: 3,
e: 4
}
};
let obj2 = JSON.parse(JSON.stringify(obj1)); // 这里采用JSON的方法(先转化成字符串,再转换回去)进行深拷贝,当然还有其他方法。
obj2.b = 5;
obj2.c.d = 6;
console.log(obj1.b); // 2
console.log(obj2.b); // 5
console.log(obj1.c.d); // 3
console.log(obj2.c.d); // 6
这样就实现了对象的深拷贝。
完全可以解决问题。但是,下面介绍一下Immutable.js。对比一下。
Immutable 特点
- 持久化数据结构(Persistent Data Structure)
- 共享结构(Structural Sharing)
- 速度快、用时短
有人做过这样一个实验
样本:1MB 的json文件(json对象),引入后深拷贝500份。
结果:
JSON.parse(JSON.stringify())方法用时:2523.555176ms
immutable.fromJs()方法用时:1295.159912ms
可以看出来Immutable的速度明显比较快。
Immutable 最常用的且最重要的API
讲API之前,先说一下Immutable 的数据类型,毕竟Immutable主要是操作数据的。
Immutable 的数据类型
- Map:无序索引集,类似 Js 中的 Object 。
- List:有序索引集,类似 Js 中的 Array 。
- OrderedMap:有序的Map,根据数据的set()进行排序。
- Set:没有重复值的集合。
- OrderedSet:有序的Set,根据数据的add进行排序。
- Stack:有序集合,支持使用unshift()和shift()添加和删除。
- Range
- … (数据类型很多,我没有列完。因为我还没学到那儿,上面列出来的最常用的也就前两个)
Immutable 最常用的且最重要的API
- fromJS() 是最常用的将原生Js数据转换为Immutable数据的方法。
const obj = Immutable.fromJS({a: 1, b: 2, c: {d: 3, e: 4}});
- toJS() 是最常用的将原生 Immutable 数据转换为 Js 数据的方法。
const obj1 = Immutable.fromJS({a: 1, b: 2, c: {d: 3, e: 4}});
const obj2 = obj1.toJS();
- get() 和 getIn() 是Immutable 数据取值的方法。
const obj1 = Immutable.fromJS({a: 1, b: 2, c: {d: 3, e: 4}});
let b = obj1.get('b');
let d = obj1.getIn(['c', 'd']); // 嵌套值获取方式
- set() 、setIn() 和 merge() 是Immutable 数据修改数据的方法。返回修改后完整的数据,不会影响旧的数据。
const obj1 = Immutable.fromJS({a: 1, b: 2, c: {d: 3, e: 4}});
// 修改单个值
const obj2 = obj1.set('b', 5); // ({a: 1, b: 5, c: {d: 3, e: 4}})
// 连写set修改同级多个值
const obj3 = obj1.set('a', 6).set('b', 5); // ({a: 6, b: 5, c: {d: 3, e: 4}})
// merge修改多个值
const obj4 = obj1.merge({a: 6, b: 5}); // ({a: 6, b: 5, c: {d: 3, e: 4}})
// getIn修改嵌套的值
const obj5 = obj1.setIn(['c', 'd'], 6); // ({a: 1, b: 2, c: {d: 6, e: 4}})
下面来说一下Immutable的优点和缺点
-
优点
1、降低Mutable带来的复杂度
原生Js的引用赋值虽然可以节省内存,但当应用复杂后,可变状态就会变成噩梦,通常一般的做法使用浅拷贝和深拷贝可以解决,但是这样造成了CPU和内存的消耗,Immutable可以很好的解决这些问题。2、节省内存空间
前面提到的结构共享,Immutable.js使用这种方式会尽量的复用内存。3、随意穿梭
因为每次数据都不一样,只要把这些数据放到一个数组中存起来,就可以作回退操作,很容易开发出撤销和回退的功能。4、拥抱函数式编程
Immutable(持久化数据结构)本身就是函数式编程的概念,纯函数式编程比面向对象更适用于前端开发。因为只要输入一致,输出必然一致,这样开发的组件更易于调试和组装。 -
缺点
1、学习成本
2、需要额外引入资源文件
3、使用过程中容易与原生对象混淆
总结一下,什么时候该用Immutable?
首先得看你的项目中,深拷贝得需求量,如果整个项目也就那么三五个地方当需要用,那完全没必要用。用JSON的方法就可以了,或者自己用递归封装一个深度拷贝的方法。
上文中提到别人做的那个实验,Immutable耗时短,得看前提,他的前提是1M得数据,拷贝500份,平常项目中得数据一般都很小,如果深拷贝用到的少,完全可以不用Immutable。如果用到的多,可以考虑用。比如react项目中,不允许直接改变state的值,这时用Immutable就很合适。
还有就是特殊功能的项目,比如说上面提到的优点第3条,项目需要做回退操作或撤销操作的功能,用Immutable实现很方便。