JSON.stringify + JSON.parse 不能直接用于深克隆,仅适用于纯数据对象;structuredClone 在支持环境下更优但不支持 function 等类型;手写需处理循环引用、原型链等边界;lodash.cloneDeep 覆盖广但有局限。

JSON.stringify + JSON.parse 能不能直接用
不能,这是最常踩的坑。它只适用于纯数据对象(plain object),一旦遇到 Date、RegExp、undefined、function、Symbol、Map、Set 或循环引用,就会出错或丢数据。
比如:JSON.stringify({ d: new Date() }) 得到 {"d":"2024-01-01T00:00:00.000Z"} —— Date 变成了字符串,类型丢失;JSON.stringify({ f: () => {} }) 直接忽略 f 字段。
适用场景仅限于:你 100% 确认对象里只有字符串、数字、布尔、null、数组和嵌套的普通对象。
structuredClone 是不是万能解
在支持它的环境里(Chrome 98+、Firefox 94+、Node.js 17.0+),structuredClone 是目前最接近“开箱即用”的方案,能正确处理 Date、Map、Set、ArrayBuffer、TypedArray、BigInt 和循环引用。
立即学习“Java免费学习笔记(深入)”;
但它不支持:function、undefined、Symbol、Promise、Error 对象,遇到会直接抛错:DataCloneError: function is not supported。
使用前务必检查运行时支持:
if (typeof structuredClone === 'function') {
try {
const clone = structuredClone(obj);
} catch (e) {
console.error('克隆失败:', e.message);
}
} else {
// 降级处理
}
手写递归克隆要注意哪些边界
手动实现时,核心是识别类型并分发处理,但容易漏掉几个关键点:
-
obj === null必须先判断,否则typeof null是"object",会误入对象分支 -
Array.isArray(obj)比obj instanceof Array更可靠,尤其跨 iframe 时 - 需要缓存已遍历的对象(用
WeakMap),否则遇到循环引用会栈溢出 -
Object.getPrototypeOf(obj)要保留,否则克隆后的对象原型链断裂(比如new Date()克隆后变 plain object)
示例片段(简化版):
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) ? [] : Object.create(Object.getPrototypeOf(obj));
map.set(obj, cloned);
for (const key in obj) {
if (Object.hasOwn(obj, key)) {
cloned[key] = deepClone(obj[key], map);
}
}
return cloned;
}
lodash.cloneDeep 和其他库的实际差异
lodash.cloneDeep 在工程中仍是稳妥选择,它覆盖了绝大多数边缘类型:支持 Map、Set、RegExp、Date、Uint8Array,甚至能克隆自定义类的实例(只要构造函数可访问且无副作用)。
但它不处理:function(原样引用)、Symbol 键(ES2015+ 的 symbol 属性默认被忽略)、prototype 上的属性(只克隆自有属性)。
注意两个常见陷阱:
- 如果对象里有
document节点或window引用,cloneDeep会静默失败或返回空对象 - 性能敏感场景(如每帧克隆大型数据结构),
cloneDeep的类型检测开销比structuredClone高不少
真正难处理的永远是“混合状态”:比如一个对象既有 Map 又有 canvas.getContext('2d'),这种没法靠通用方案解决,得按业务定制序列化逻辑。











