浅拷贝只复制引用,深拷贝递归创建新对象;JSON.parse(JSON.stringify())有严重限制,structuredClone()是现代首选方案,兼容老环境需手写递归或用Lodash的_.cloneDeep。

浅拷贝只复制引用,深拷贝复制整个对象结构
JavaScript 中的 let a = {x: 1} 和 let b = a 是典型的浅拷贝:变量 b 持有的只是对同一内存地址的引用。改 b.x,a.x 也会变。深拷贝则会递归创建新对象,确保修改副本不影响原对象。
JSON.parse(JSON.stringify()) 能用但有严重限制
这是最常被误用的“深拷贝”方案,它看似简单,实则在多种场景下直接失效:
-
undefined、function、Symbol、BigInt类型会被静默丢弃 - 循环引用会抛出
TypeError: Converting circular structure to JSON - Date、RegExp、Map、Set、TypedArray 等内置对象变成空对象或字符串(如
new Date()→"2024-01-01T00:00:00.000Z") - 原型链和不可枚举属性全部丢失
const obj = {
date: new Date(),
regex: /abc/,
fn: () => {},
undef: undefined,
map: new Map([[1, 'a']])
};
console.log(JSON.parse(JSON.stringify(obj)));
// { date: "2024-01-01T00:00:00.000Z", regex: {}, map: {} } —— 全错
structuredClone() 是现代浏览器首选方案
Chrome 98+、Firefox 94+、Safari 15.4+ 已支持 structuredClone(),它能正确处理大多数内置类型(包括 Date、Map、Set、ArrayBuffer、TypedArray、RegExp),且支持循环引用,是目前最接近“开箱即用”的深拷贝方法:
- 不支持
function、undefined、Symbol、BigInt(这些本就不在 structured clone algorithm 规范中) - 不保留原型链(所有对象都变成 plain object)
- 不能拷贝带有私有字段的类实例(
#field会丢失)
const original = {
arr: [1, 2],
date: new Date('2023-01-01'),
map: new Map([['key', 'value']]),
nested: { x: 1 }
};
const copy = structuredClone(original);
copy.nested.x = 99;
console.log(original.nested.x); // 1 —— 深拷贝生效
需要兼容老环境?手写递归或用 Lodash 的 _.cloneDeep
如果必须支持 IE 或旧版 Safari,自己实现一个基础版递归深拷贝比硬套 JSON 更可控:
立即学习“Java免费学习笔记(深入)”;
- 检查
typeof和Array.isArray()区分类型 - 对
null、基本类型直接返回(它们不可变) - 对对象/数组递归调用,用
Object.keys()或for...in遍历可枚举属性(注意跳过原型链) - 遇到循环引用需用
WeakMap缓存已拷贝过的对象并复用
生产环境更推荐直接引入 lodash.clonedeep:它覆盖了几乎所有边界情况(包括 Map、Set、Buffer、类实例等),且经过大量测试。注意它也不拷贝函数和 Symbol 键——这点和 structuredClone() 一致,不是 bug,是设计选择。
真正难的从来不是“怎么拷”,而是“要不要拷”:很多所谓“深拷贝需求”,其实源于状态管理混乱或副作用未隔离。先确认是否真的需要深拷贝,再选方案。否则,修好 JSON.stringify() 的坑,可能只是把问题从运行时推迟到某个更诡异的时刻。










