浅拷贝 vs 深拷贝
对比浅拷贝和深拷贝的引用层级差异,以及面试中常问的使用边界。
#type / concept
#status / growing
#tech / dev / frontend
浅拷贝 vs 深拷贝
一句话定义
浅拷贝只复制对象的第一层属性,嵌套对象仍是共享引用;深拷贝递归复制所有层级,产出完全独立的副本。
核心机制:引用 vs 值
JavaScript 中对象是引用类型。赋值 const b = a 只是让 b 指向同一块内存,不产生新对象。
赋值: a ──→ { x: 1, inner: { y: 2 } } ←── b (同一对象)
浅拷贝: a ──→ { x: 1, inner: ──┐ }
└→ { y: 2 } ←── b.inner (共享)
b ──→ { x: 1, inner: ──┘ }
深拷贝: a ──→ { x: 1, inner: { y: 2 } } (独立)
b ──→ { x: 1, inner: { y: 2 } } (独立)
浅拷贝方法
const original = { name: 'Alice', scores: [90, 85] };
// 1. Object.assign
const clone1 = Object.assign({}, original);
// 2. 展开运算符
const clone2 = { ...original };
// 3. Array.slice / Array.from(数组)
const arr = [1, [2, 3]];
const arrClone = arr.slice();
// 或
const arrClone2 = [...arr];
证明是浅拷贝:
clone1.scores.push(100);
console.log(original.scores); // [90, 85, 100] — 被影响了!
深拷贝方法
// 1. structuredClone(推荐)
const deep1 = structuredClone(original);
// 2. JSON(有限制)
const deep2 = JSON.parse(JSON.stringify(original));
// 3. 手写递归 / lodash _.cloneDeep
嵌套突变示例:为什么它重要
const state = {
user: { name: 'Alice', address: { city: 'Beijing' } },
items: [1, 2, 3]
};
// 浅拷贝更新(Redux-style 错误示范)
const newState = { ...state };
newState.user.name = 'Bob';
console.log(state.user.name); // "Bob" — 原 state 被污染了!
// 正确做法:逐层深拷贝
const correctState = {
...state,
user: { ...state.user, address: { ...state.user.address } },
items: [...state.items]
};
选择建议
| 场景 | 推荐 |
|---|---|
| 对象只有一层(扁平) | 浅拷贝({ ...obj }) |
| 需要修改嵌套结构 | 深拷贝或不可变更新(Immer) |
| Redux / 状态管理 | 不可变更新(只拷贝变更路径) |
| 数据传输 / 快照 | structuredClone |
边界与常见误解
- 展开运算符
{ ...obj }不是深拷贝:这是最常见的误解,它只是浅拷贝。 - 浅拷贝够用的条件:当你只操作顶层属性、或使用不可变数据流(每次替换整个子对象)时。
- 性能:深拷贝有递归开销,数据量大时优先考虑结构共享(如 Immer 的 Copy-on-Write)。