跳到主要内容

不可变数据

不可变性(Immutability)是函数式编程的核心原则之一,指的是数据一旦创建就不能被修改。

什么是不可变性?

不可变数据在创建后不能被改变。任何修改操作都会返回一个新的数据,而不是修改原始数据。

// 可变(Mutable)
const arr = [1, 2, 3];
arr.push(4); // 修改原数组

// 不可变(Immutable)
const arr = [1, 2, 3];
const newArr = [...arr, 4]; // 创建新数组

为什么需要不可变性?

  1. 可预测性:数据不会意外改变
  2. 易于调试:可以追踪数据的变化历史
  3. 并发安全:没有数据竞争问题
  4. 时间旅行调试:可以轻松回滚状态
  5. 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;
});

不可变性的最佳实践

  1. 使用 const 声明变量
  2. 使用展开运算符复制数据
  3. 使用 map/filter/reduce 替代循环
  4. 使用 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 等库使用结构共享优化内存
  • 选择性使用:不是所有数据都需要不可变
  • 大数据集:对于大量数据,使用专门的不可变库