Immutable学习笔记(整合了相关博客的内容)
[TOC]
我们在使用react的redux的时候,我们要求所有state只能由一个store存储,然后每一次修改state,都需要返回一个新的state,以上是我们的需求。而Immutable就正好迎合了我们的这部分需求。
以往我们都是用类似下面的代码来重新拼接返回一个新的state
return Object.assign({},state, {
todos: [
...state.todos,
{
text: action.text,
completed: false
}
]
})
现在我们大可使用Immutable来解决这个问题。
什么是 Immutable Data
Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。Immutable 实现的原理是 Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了 Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。请看下面动画:
Immutable的优点
- 使得数据可以被回溯,由于每次更改都不改变原数据,类似撤回等功能很容就可以实现
- 节省内存,Immutable.js 使用了 Structure Sharing 会尽量复用内存,甚至以前使用的对象也可以再次被复用。没有被引用的对象会被垃圾回收。
- Immutable 本身就是函数式编程中的概念,纯函数式编程比面向对象更适用于前端开发。因为只要输入一致,输出必然一致,这样开发的组件更易于调试和组装。
Immutable使用
两个Immutable对象的比较
用is()或者equal(),这是进行值比较
let map1 = Map({a:1, b:1, c:1});
let map2 = Map({a:1, b:1, c:1});
console.log(map1 === map2); //false
console.log(map1.equals(map2)); //true
console.log(is(map1, map2)); //true
Immutable.is 比较的是两个对象的 hashCode 或 valueOf(对于 JavaScript 对象)。由于 immutable 内部使用了 Trie 数据结构来存储,只要两个对象的 hashCode 相等,值就是一样的。这样的算法避免了深度遍历比较,性能非常好。
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
Map,List,fromJs,Seq区别
- Map()接收的是Object
- List()接收的是Array
- fromJs接收的是Object或者Array
- Seq接收Object或者Array
这里主要说Seq:
Seq 是不可变的 — 一旦 Seq 创建便不能修改, 添加, 排列或其它修改. 任何修改操作都会生成一个新的 Seq.
Seq 是惰性的 — Seq 尽可能少的去相应函数调用。
var oddSquares = Seq.of(1,2,3,4,5,6,7,8)
.filter(x => { console.log("$", x); return x % 2}).map(x => {console.log("#"); return x * x});
console.log(oddSquares.get(1));
/*---输出---*/
// $ 1
// $ 2
// $ 3
// #
// 9
//此处的filter执行了3次,map执行了1次,后续的不执行
然后所有的集合都可以用toSeq()转成Seq
批量修改
由于每一次修改都会生成一个新的immutable对象有一定的性能损耗,可以使用withMutations方法,使批量修复在临时集合拷贝上发生。
var list1 = List.of(1,2,3);
var list2 = list1.withMutations(function (list) {
list.push(4).push(5).push(6);
});
console.log(list1.size === 3);
console.log(list2.size === 6);
注: immutable 还提供了 asMutable 和 asImmutable , 但是只有在 withMutation 不能满足情况的时候使用。注意不要返回一个可变的拷贝,会导致非预知的行为。
重要!:只有特殊的几个方法能使用 withMutation 其中包括 set, push 和 pop。这些方法能直接作用于内部数据结构,而方法如 map, filter, sort, splice 一定不会修改可修改集合并返回新的不变集合。
List
构建List,可用List(Array) or List.of(Values)