实现不可变数据集合的核心是每次修改返回新对象。使用展开运算符可手动实现对象和数组的不可变操作,如 {...obj, key: value} 和 [...arr, item];封装 ImmutableList 类能通过 add、remove 方法返回新实例;推荐使用 Immer 或 Immutable.js 等库处理复杂结构,Immer 允许“可变”语法生成不可变状态,Immutable.js 提供不可变 List、Map 类型;关键原则包括不修改原数据、所有变更返回新实例、用 Object.freeze 防止意外修改,并在性能敏感场景采用结构共享优化。

实现一个基于 JavaScript 的不可变数据集合,核心是确保每次修改都返回新对象,而不是改变原对象。虽然原生 JS 对象和数组是可变的,但可以通过一些模式和工具来构建不可变性。
使用展开运算符(Spread Operator)手动实现不可变操作
对于简单对象和数组,利用展开运算符可以避免直接修改原数据。
对象更新: 不要直接赋值,而是创建新对象。
const originalObj = { name: 'Alice', age: 25 };
const updatedObj = { ...originalObj, age: 26 }; // 新对象
数组操作: 使用 filter、map、concat 等不改变原数组的方法。
const list = ['a', 'b'];
const newList = [...list, 'c']; // 添加
const filtered = list.filter(item => item !== 'a'); // 删除
封装不可变集合类
你可以定义一个简单的类来封装不可变逻辑,比如一个不可变列表。
class ImmutableList {
constructor(values = []) {
this._values = Object.freeze([...values]);
}
add(value) {
return new ImmutableList([...this._values, value]);
}
remove(index) {
const newArr = this._values.filter((_, i) => i !== index);
return new ImmutableList(newArr);
}
toArray() {
return this._values;
}
}
这样每次调用 add 或 remove 都会返回新的实例,原始数据不受影响。
立即学习“Java免费学习笔记(深入)”;
使用成熟的不可变库(如 Immer 或 Immutable.js)
手动维护不可变逻辑在复杂结构中容易出错。推荐使用专门的库简化操作。
Immer: 允许你用“可变”语法写代码,但生成不可变结果。
import { produce } from 'immer';
const baseState = { user: { name: 'Bob' } };
const nextState = produce(baseState, draft => {
draft.user.name = 'Charlie'; // 看似可变,实际不可变
});
produce 内部使用代理和结构共享,高效生成新状态。
Immutable.js: 提供 List、Map、Set 等不可变数据类型。
import { List } from 'immutable';
const list1 = List([1, 2]);
const list2 = list1.push(3); // 返回新 List
console.log(list1.toArray()); // [1, 2]
console.log(list2.toArray()); // [1, 2, 3]
关键原则总结
- 永远不修改输入参数或内部状态
- 所有变更方法都返回新实例
- 使用 Object.freeze 可防止意外修改(浅冻结)
- 深层嵌套结构需递归冻结或依赖库处理
- 考虑性能:频繁操作大对象时,使用结构共享优化










