深拷贝与浅拷贝
JavaScript深拷贝与浅拷贝详解:structuredClone、JSON方案缺陷与自定义实现。
1. 浅拷贝
1.1 浅拷贝方法
const original = { a: 1, b: { c: 2 } };
// Object.assign
const copy1 = Object.assign({}, original);
// 展开运算符
const copy2 = { ...original };
// Array.prototype.slice(数组)
const arr = [1, 2, 3];
const arrCopy = arr.slice();
// Array.from
const arrCopy2 = Array.from(arr);
1.2 浅拷贝的问题
const original = { a: 1, b: { c: 2 } };
const copy = { ...original };
copy.a = 100; // 不影响 original
copy.b.c = 200; // 影响 original!b 是引用类型
console.log(original.b.c); // 200 — 浅拷贝只复制引用
2. JSON 序列化方案
2.1 基本用法
const original = { name: 'Alice', age: 30, hobbies: ['reading', 'coding'] };
const deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.hobbies.push('gaming');
console.log(original.hobbies); // ['reading', 'coding'] — 不受影响
2.2 JSON 方案的缺陷
| 缺陷 | 示例 |
|---|---|
忽略 undefined | { a: undefined } → {} |
| 忽略函数 | { fn: () => {} } → {} |
| 忽略 Symbol | { [Symbol('x')]: 1 } → {} |
| 忽略原型链 | new Date() → 字符串 |
| 循环引用报错 | obj.self = obj → TypeError |
| 特殊对象丢失 | Map, Set, RegExp, Buffer → {}或异常 |
NaN/Infinity | 变成 null |
| 日期变字符串 | new Date() → "2026-06-14T..." |
const obj = {
undefined: undefined,
fn: () => console.log('hello'),
date: new Date(),
regex: /pattern/gi,
map: new Map([['key', 'value']]),
set: new Set([1, 2, 3]),
nan: NaN,
infinity: Infinity,
bigInt: 9007199254740991n, // TypeError!
};
JSON.parse(JSON.stringify(obj));
// { date: "2026-06-14T...", regex: {}, map: {}, set: {}, nan: null, infinity: null }
3. structuredClone
3.1 基本用法
const original = {
date: new Date(),
regex: /pattern/gi,
map: new Map([['key', 'value']]),
set: new Set([1, 2, 3]),
arrayBuffer: new ArrayBuffer(8),
nested: { a: { b: 1 } },
};
const clone = structuredClone(original);
clone.nested.a.b = 999;
console.log(original.nested.a.b); // 1 — 深拷贝成功
clone.map.set('new', 'entry');
console.log(original.map.has('new')); // false — Map 独立
3.2 支持的类型
| 类型 | structuredClone | JSON |
|---|---|---|
| 基本类型 | ||
Date | (变字符串) | |
RegExp | (变 {}) | |
Map / Set | (变 {}) | |
ArrayBuffer | ||
TypedArray | ||
Blob | ||
ImageData | ||
| 循环引用 | ||
undefined | ||
| 函数 | ||
Symbol | ||
| DOM 节点 | ||
Error |
3.3 循环引用
const obj = { name: 'circular' };
obj.self = obj;
const clone = structuredClone(obj); // 正常工作
console.log(clone.self === clone); // true
console.log(clone.self === obj); // false
3.4 转移对象
const buffer = new ArrayBuffer(1024);
const clone = structuredClone(buffer, { transfer: [buffer] });
// buffer 被"转移"到 clone,原始 buffer 变为 0 字节
console.log(buffer.byteLength); // 0
console.log(clone.byteLength); // 1024
4. 自定义深拷贝
4.1 基础实现
function deepClone(obj, cache = new WeakMap()) {
// 基本类型
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 循环引用
if (cache.has(obj)) {
return cache.get(obj);
}
// Date
if (obj instanceof Date) {
return new Date(obj);
}
// RegExp
if (obj instanceof RegExp) {
return new RegExp(obj.source, obj.flags);
}
// Map
if (obj instanceof Map) {
const clone = new Map();
cache.set(obj, clone);
obj.forEach((value, key) => {
clone.set(deepClone(key, cache), deepClone(value, cache));
});
return clone;
}
// Set
if (obj instanceof Set) {
const clone = new Set();
cache.set(obj, clone);
obj.forEach((value) => {
clone.add(deepClone(value, cache));
});
return clone;
}
// Array
if (Array.isArray(obj)) {
const clone = [];
cache.set(obj, clone);
obj.forEach((item, index) => {
clone[index] = deepClone(item, cache);
});
return clone;
}
// 普通对象
const clone = Object.create(Object.getPrototypeOf(obj));
cache.set(obj, clone);
// 复制所有自有属性(包括 Symbol)
Reflect.ownKeys(obj).forEach((key) => {
clone[key] = deepClone(obj[key], cache);
});
return clone;
}
4.2 保留函数引用
function deepCloneWithFunctions(obj, cache = new WeakMap()) {
if (typeof obj === 'function') {
// 函数不克隆,直接返回引用
return obj;
}
// 其余逻辑同上...
}
5. 性能对比
数据量: 10000 个嵌套对象
| 方法 | 耗时 | 支持类型 |
| ----------------- | -------- | -------- |
| JSON.parse/stringify | ~15ms | 有限 |
| structuredClone | ~8ms | 广泛 |
| 自定义 deepClone | ~25ms | 最灵活 |
| 浅拷贝 (...) | ~1ms | 仅一层 |
选择建议:
- 简单数据(纯 JSON 兼容):
JSON.parse(JSON.stringify())或structuredClone - 包含 Date/Map/Set/RegExp:
structuredClone - 包含函数/特殊对象:自定义
deepClone - 仅一层嵌套:浅拷贝
...