不可变数据
不可变性(Immutability)是函数式编程的核心原则之一,指的是数据一旦创建就不能被修改。
什么是不可变性?
不可变数据在创建后不能被改变。任何修改操作都会返回一个新的数据,而不是修改原始数据。
// 可变(Mutable)
const arr = [1, 2, 3];
arr.push(4); // 修改原数组
// 不可变(Immutable)
const arr = [1, 2, 3];
const newArr = [...arr, 4]; // 创建新数组
为什么需要不可变性?
- 可预测性:数据不会意外改变
- 易于调试:可以追踪数据的变化历史
- 并发安全:没有数据竞争问题
- 时间旅行调试:可以轻松回滚状态
- React 优化:便于实现 shouldComponentUpdate
JavaScript 中的不可变操作
数组的不可变操作
const arr = [1, 2, 3];
// 添加元素
const newArr = [...arr, 4];
// 删除元素
const withoutSecond = [...arr.slice(0, 1), ...arr.slice(2)];
// 修改元素
const modified = arr.map((x, i) => i === 1 ? 99 : x);
// 过滤元素
const evens = arr.filter(x => x % 2 === 0);
对象的不可变操作
const obj = { name: 'John', age: 30 };
// 添加/修改属性
const updated = { ...obj, age: 31, city: 'NYC' };
// 删除属性
const { age, ...withoutAge } = obj;
// 嵌套对象更新
const nested = {
user: { name: 'John', address: { city: 'NYC' } }
};
const updatedNested = {
...nested,
user: {
...nested.user,
address: {
...nested.user.address,
city: 'LA'
}
}
};
不可变数据结构库
Immutable.js
const { Map, List } = require('immutable');
const map = Map({ a: 1, b: 2 });
const newMap = map.set('b', 3);
const list = List([1, 2, 3]);
const newList = list.push(4);
Immer
import produce from 'immer';
const nextState = produce(baseState, draft => {
draft.user.age = 31;
});
不可变性的最佳实践
- 使用 const 声明变量
- 使用展开运算符复制数据
- 使用 map/filter/reduce 替代循环
- 使用 Object.freeze 深度冻结(开发环境)
function deepFreeze(obj) {
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object' && obj[key] !== null) {
deepFreeze(obj[key]);
}
});
return Object.freeze(obj);
}
性能考虑
- 结构共享:Immutable.js 等库使用结构共享优化内存
- 选择性使用:不是所有数据都需要不可变
- 大数据集:对于大量数据,使用专门的不可变库