JavaScript不可变数据
2021-11-05 本文已影响0人
JakeBless
什么是数据不可变
数据一旦创建,就不可更改
immutable对象是不可直接赋值的对象,它可以有效的避免错误赋值的问题
什么是可变数据? 比如javascript的绝大多数数据(let var命名的变量,对象等)其实就是可变数据
// 比如说这种情况
var a = {
x: 1
}
var b = a
b.x = 2
console.log(a.x)
// 再来举一个react的例子
https://codesandbox.io/s/dreamy-panka-de7u9?file=/src/App.js:153-171
// 那常规得解决方式无非是深拷贝
var b = clouedeep(a)
b.x = 2
但是深拷贝在频繁的操作当中,比较消耗内存,性能很差
那如果尝试使用immutable呢?
immutable库
immutable.js
immutable介绍 对immutable对象的任何修改或删除添加都会返回一个新的immutable对象。
概念: Map List
import { Map } from 'immutable';
let a = Map({
select: 'users',
filter: Map({ name: 'Cam' })
})
let b = a.set('select', 'people');
a === b; // false
a.get('filter') === b.get('filter'); // true
immer.js
immer当中没有单独的数据类型
import { produce } from 'immer'
var a = {
x: 1
}
var b = immer.produce()
b = immer.produce((draft) => {
draft.x = 2
})
// b.x = 2
console.log(b) 答案是什么?
有人会说用...展开语法不就解决了react的这个setState的问题吗?
let [t, setT] = useState({ a: 1 });
setT((state) => {
...t,
t.a: 2
});
// 但下面这种情况呢?
let [adress, setAdress] = useState({
phone: 123,
city: {
code: '1234',
area: 'beijing'
}
})
setAdress({
...address,
city: {
...address.city,
area: "Chengd"
}
})
但是如果使用immer解决上述React遇到的问题
let [t, setT] = useState({ a: 1 });
setT(
produce(t, (t) => {
t.a = 2;
})
);
// 第二个复杂数据的例子
setAdress(prevState => produce(prevState, (draftState) = > {
draftState.address.city.area = 'JingAn'
draftState.address.postcode += 10
})
// 进一步简写
setAdress(
produce((draftState) => {
draftState.address.city.area = 'JingAn'
draftState.address.postcode += 10
})
)
不可变数据的应用范围
-
react setState
import { produce } from 'immer' let [t, setT] = useState({ a: 1 }); setT( produce(t, (t) => { t.a = 2; } ) );
if use use-immer
import useImmer from 'use-immer' const [immer, setImmer] = useImmer({ name: "Ming", sex: 'femal', age: 18 }) setImmmer(draft => { draf.age ++ })
-
react redux
下面介绍一下redux的immer应用(可以参考redux的例子里的src/reducers/index.js尝试一下)
https://github.com/reactjs/redux/tree/master/examples/counter
// 不使用 immer
export default (state = { count: 0 }, action) => {
switch (action.type) {
case "INCREMENT":
return {
count: state.count + 1
};
case "DECREMENT":
return {
count: state.count - 1
};
default:
return state;
}
};
// 使用 immer 后
export default (state = { count: 0 }, action) =>
produce(state, (draftstate) => {
if (action.type == "INCREMENT") {
draftstate.count += 1;
} else if (action.type == "DECREMENT") {
draftstate.count -= 1;
}
});
-
复制粘贴 前进后退 时间穿梭
var a = {x: 1} var history = [] history.push(a) history.push(produce(a => { a.x = 2 })