JavaScript浅拷贝只复制第一层属性引用,深拷贝需递归处理所有层级;JSON.parse(JSON.stringify())因丢失函数、undefined等类型而不适用于生产环境。

JavaScript 中的浅拷贝只复制对象第一层属性的引用,深拷贝则递归复制所有层级——但直接用 JSON.parse(JSON.stringify(obj)) 会丢函数、undefined、Symbol、Date、RegExp 等,生产环境必须避开这个坑。
浅拷贝:哪些方法只复制一层?
浅拷贝本质是“新对象 + 原对象顶层属性值的赋值”,对嵌套对象仍共享内存地址。
-
Object.assign({}, obj):忽略原型链上的属性,不处理 getter/setter -
{...obj}(展开语法):同上,且无法拷贝不可枚举属性 -
Array.prototype.slice()或[...arr]:仅适用于数组,对嵌套数组元素仍是浅拷贝
常见错误现象:const a = {x: {y: 1}}; const b = {...a}; b.x.y = 2; 执行后 a.x.y 也变成 2。
深拷贝:为什么 JSON.parse(JSON.stringify()) 不可靠?
它会跳过 undefined、function、Symbol,把 Date 变成字符串,RegExp 变成空对象,NaN 变成 null,遇到循环引用直接报错 TypeError: Converting circular structure to JSON。
立即学习“Java免费学习笔记(深入)”;
适用场景仅限纯数据对象(POJO),且确认不含上述类型。例如配置项、API 响应体预处理可临时用,但不能用于状态管理或通用工具函数。
手写简单深拷贝:如何处理常见边界类型?
核心思路是类型判断 + 递归,同时用 WeakMap 解决循环引用问题。
function deepClone(obj, map = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (map.has(obj)) return map.get(obj);
const cloned = Array.isArray(obj) ? [] : {};
map.set(obj, cloned);
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
cloned[key] = deepClone(obj[key], map);
}
}
return cloned;
}
这个版本能处理数组、普通对象、null、基本类型,支持循环引用;但依然不处理 Date、RegExp、Map、Set 等——如果业务需要,得在判断分支里单独处理,比如:
- 遇到
obj instanceof Date→ 返回new Date(obj) - 遇到
obj instanceof RegExp→ 返回new RegExp(obj) - 遇到
obj instanceof Map→ 遍历obj.entries()重建
Lodash 的 cloneDeep 和结构化克隆 API 怎么选?
现代浏览器已支持 structuredClone()(Chrome 98+、Firefox 94+、Safari 15.4+),它能正确处理 Date、RegExp、Map、Set、ArrayBuffer、TypedArray,也支持循环引用,且比手写快。但它不支持函数、undefined、Symbol,且 Node.js 直到 v18.13.0 才默认启用(需加 --enable-structured-clone 标志)。
Lodash 的 cloneDeep 兼容性更好,但体积大(约 10KB min+gzip),若项目已引入 Lodash 可直接用;否则优先考虑 structuredClone + 特殊类型 fallback。
真正容易被忽略的是:深拷贝永远有成本。高频调用时,先想清楚是否真需要拷贝——有时用不可变更新(如 Immer)、状态分片或引用隔离更合适。










