immutable.js 不可变数据
js中的对象和数组属于引用数据类型,一个对象赋值给另外一个对象,其实是引用地址的赋值,两个变量同时指向同一个对象。
如果需要进行修改对象,那么两个对象会同时改变,
什么是不可变
对引用数据进行改变的时候,不会更改原数据,而是返回一个新的数据
通过...
进行创建,这是一个浅拷贝
js
const arr1 = [1,2,3]
const arr2 = [...arr1]
arr2.push(4) // arr2[1,2,3,4] arr1[1,2,3]
如果需要进行多层的,那么会失效,需要使用深拷贝
ini
const arr1 = [{a:2},2,3]
const arr2 = [...arr1]
arr2[0].a = 1 // arr2[{a:1},2,3,4] arr1[{a:1},2,3]
深拷贝存在性能问题,每次深拷贝需要对对象递归复制,内存多出了很多重复相同数据,增加内存占用
简单的深拷贝实现
js
/**
* @description 深拷贝
* */
function deepClone(obj, map = new WeakMap()) {
// 判断基础类型
if (typeof obj !== "object" || obj == null) return obj;
// 解决循环引用
if (map.has(obj)) return map.get(obj);
let target = {};
map.set(obj, target);
// 判断Map
if (obj instanceof Map) {
target = new Map();
obj.forEach((v, k) => {
const k1 = deepClone(k, map);
const v1 = deepClone(k, map);
target.set(k1, v1);
});
}
// 判断Set
if (obj instanceof Set) {
target = new Set();
obj.forEach(v => {
target.add(deepClone(v, map));
});
}
// 判断array
if (obj instanceof Array) {
target = obj.map(item => deepClone(item,map));
}
// 判断Object
for (let key in obj) {
target[key] = deepClone(obj[key],map);
}
return target;
}
优点
-
可以防止数据突变带来的不可预测。
-
可以提高操作性能
使用方法
提供了多个数据结构,常用的是List和Map
设置数据(set)
js
import { List, Map } from "immutable"
const l1 = new List(["a","b"])
const l2 = l1.set(0,"c") // List["c","b"]
const m1 = new List({a:1,b:2})
const l2 = l1.set("a",3) // Map{a:3,b:2}
获取数据(get)
js
import { List, Map } from "immutable"
const l1 = new List(["a","b"])
const l2 = l1.set(0,"c") // List["c","b"]
l1.get(0) // a
const m1 = new List({a:1,b:2})
const m2 = l1.set("a",3) // Map{a:3,b:2}
m2.get("a") // 3
合并数据(merge)
js
import { List } from "immutable"
const l1 = new List(["a","b"]) // ["a","b"]
const l2 = l1.set(0,"c") // List["c","b"]
const l3 = l1.merge(l2) // List ["a","b","c","b"]
const m1 = new Map({a:1,b:2})// Map{a:1,b:2}
const m2 = new Map({c:3,d:4})// Map{c:1,d:2}
const l3 = m1.merge(m2) // // Map{a:1,b:2,c:1,d:2}
删除数据(remove)
js
const l1 = new List(["a","b"]) // ["a","b"]
const l2 = l1.set(0,"c") // List["c","b"]
l2.remove(0) // List["b"]
更新(update)
js
const l1 = new List(["a","b"]) // ["a","b"]
const l2 = l1.set(0,"c") // List["c","b"]
l2.update(0,target => target+1) // // List["c1","b"]
const m1 = new Map({a:1,b:2})// Map{a:1,b:2}
m1.update("a", target => target*2 ) // Map{a:2,b:2}
数据转换(fromJs)
不支持嵌套,需要转换
js
import { fromJs, Map } from "immutable"
const m1 = new Map({a:{b:{c:1}}}) // Map{a:[object Obejct]}
const m2 = fromJs({a:{b:{c:1}}}) // Map({a:{b:{c:1}}})
const m3 = m2.setIn(["a","b","c"],100) // Map({a:{b:{c:100}}})
数据比较(is)
判断不可变数据是否相等
js
import { fromJs, is } from "immutable"
const m1 = fromJs({a:{b:{c:1}}}) // Map({a:{b:{c:1}}})
const m2 = m2.setIn({a:{b:{c:1}}}) // Map({a:{b:{c:100}}})
和react结合
react中更新数据需要用到不可变数据,内部使用Object.is
判断两个对象是否相同,如果不相同,那么就会进入diff过程
jsx
import { fromJs,is } from "immutable"
class App extends Component {
constructor() {
super()
this.updateName = this.updateName.bind(this)
this.state ={
person:fromJs({name:"张三"})
}
}
shouldComponentUpdate(nextProps,nextState) {
return !is(this.state.person,nextState.person)
}
updateName() {
this.setState({
person:this.state.person.set("name","李四")
})
}
render() {
console.log("render")
return ( <div>
<p>{this.state.person.get("name")}</p>
<button onClick={this.updateName}>修改</button>
</div>)
}
}
结尾
@react/toolkit内部集成了不可变数据库,有必要学习和认识一下。
我认为简单的数据正常使用扩展运算符就可以解决,至于是否需要使用immutable
这个库。取决于大leader的想法了