深拷贝函数需区分类型、递归处理对象/数组、避免循环引用;基础版支持null、原始类型、Object、Array;增强版用WeakMap检测循环引用,确保安全与内存友好。

手写一个支持基本数据类型的递归深拷贝函数,核心是**区分类型、递归处理对象/数组、避免循环引用**。下面是一个简洁、健壮、可直接使用的实现。
基础版本(支持 null、undefined、原始类型、Object、Array)
这个版本不处理 Date、RegExp、Map、Set 等特殊对象,但覆盖了绝大多数日常场景:
function deepClone(obj) {
// 处理 null 和非对象(包括原始类型、函数、symbol、bigint)
if (obj === null || typeof obj !== 'object') {
return obj;
}
<p>// 区分数组和普通对象
const result = Array.isArray(obj) ? [] : {};</p><p>for (const key in obj) {
// 只拷贝自身可枚举属性
if (Object.prototype.hasOwnProperty.call(obj, key)) {
result[key] = deepClone(obj[key]);
}
}</p><p>return result;
}✅ 支持:
- 基本类型(string / number / boolean / undefined / null / symbol / bigint)
- 普通对象({})和数组([])
- 嵌套任意层级
⚠️ 注意:
- 函数、Symbol、BigInt 会原样返回(属于“浅拷贝”行为,但符合 JS 原始语义——它们不可变且无需深拷)
- 不处理循环引用,遇到会爆栈(RangeError)
增强版本(支持循环引用检测)
用 WeakMap 记录已克隆的对象,防止无限递归:
function deepClone(obj, seen = new WeakMap()) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
<p>// 检查是否已克隆过该对象
if (seen.has(obj)) {
return seen.get(obj);
}</p><p>const result = Array.isArray(obj) ? [] : {};
seen.set(obj, result);</p><p>for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
result[key] = deepClone(obj[key], seen);
}
}</p><p>return result;
}✅ 新增能力:
- 安全处理 self-referencing 对象(如 let a = {}; a.self = a;)
- WeakMap 保证内存友好,不影响垃圾回收
常见误区与注意事项
-
不用
JSON.parse(JSON.stringify()):丢失 function、undefined、Symbol、NaN、Infinity、Date、RegExp,且无法处理循环引用 -
不盲目遍历所有属性:用
hasOwnProperty过滤原型链属性,避免污染 -
区分 Array 和 Object:直接用
Array.isArray(),不要靠typeof [] === 'object'判断 -
不处理不可枚举属性或 getter/setter:如需支持,需用
Object.getOwnPropertyDescriptors+Object.defineProperties
简单测试用例
验证一下效果:
const original = {
a: 1,
b: { c: 2, d: [3, 4] },
e: () => {},
f: Symbol('s')
};
<p>const cloned = deepClone(original);
console.log(cloned.b.d === original.b.d); // false → 深拷贝成功
console.log(cloned.e === original.e); // true → 函数不拷贝(合理)
console.log(cloned.f === original.f); // true → Symbol 不拷贝(合理)不复杂但容易忽略细节。按需选择基础版或带循环引用检测的版本即可。


















