深拷贝函数通过类型识别、递归复制和weakmap缓存解决循环引用,支持string、number、boolean、null、undefined、symbol、bigint、object、array、date、regexp、map、set等所有基础类型。

手写一个支持所有基础类型的深拷贝函数,核心在于正确处理各种数据类型(尤其是引用类型)的递归复制,同时避免循环引用导致的栈溢出。
识别并分类数据类型
JavaScript 中需要区分基本类型(string、number、boolean、null、undefined、symbol、bigint)和引用类型(object、array、date、regexp、map、set、function 等)。基本类型直接返回,引用类型需逐层克隆。
- 用
typeof判断基本类型,但注意typeof null === 'object',需单独处理 - 用
Object.prototype.toString.call()精确识别内置对象类型,如[object Date]、[object RegExp]、[object Map]等 -
function通常不深拷贝(无法可靠序列化),可选择浅拷贝引用或忽略(按需决定)
处理循环引用
若对象存在自引用或相互引用(如 a.b = a),递归会无限进行。必须记录已拷贝过的对象及其对应副本,遇到重复引用时直接返回已有副本。
- 使用
WeakMap存储源对象 → 副本映射(键为对象,安全且不阻止垃圾回收) - 每次进入对象拷贝前,先查 WeakMap;命中则直接返回缓存副本
- 拷贝新对象前,先在 WeakMap 中预先建立映射,再递归处理其属性,防止自身引用死循环
逐类型实现克隆逻辑
对每种引用类型采用最符合语义的构造方式:
-
普通对象 / 数组:用
Array.isArray()区分,分别用{}或[]创建新容器,再遍历属性/元素递归拷贝 -
Date:
new Date(obj.getTime()) -
RegExp:
new RegExp(obj.source, obj.flags) - Map / Set:新建实例,遍历键值对或元素,递归拷贝键与值(Map 的键也可能是对象,需深拷贝)
-
TypedArray(如 Uint8Array):用构造函数传入原数组,如
new obj.constructor(obj)
完整示例(精简可运行版)
以下是一个覆盖主流基础类型的深拷贝函数,不含 function 拷贝(保留原引用),支持循环引用:
function deepClone(obj, seen = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (seen.has(obj)) return seen.get(obj);
const tag = Object.prototype.toString.call(obj);
let clone;
switch (tag) {
case '[object Date]': clone = new Date(obj.getTime()); break;
case '[object RegExp]': clone = new RegExp(obj.source, obj.flags); break;
case '[object Array]': clone = []; break;
case '[object Map]': clone = new Map(); break;
case '[object Set]': clone = new Set(); break;
case '[object Object]': clone = {}; break;
default: return obj; // 如 ArrayBuffer、Error 等按需扩展
}
seen.set(obj, clone);
if (Array.isArray(obj)) {
obj.forEach((item, i) => clone[i] = deepClone(item, seen));
} else if (obj instanceof Map) {
obj.forEach((value, key) => clone.set(deepClone(key, seen), deepClone(value, seen)));
} else if (obj instanceof Set) {
obj.forEach(item => clone.add(deepClone(item, seen)));
} else {
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
clone[key] = deepClone(obj[key], seen);
}
}
}
return clone;
}
它能正确处理 string、number、boolean、null、undefined、symbol、bigint、object、array、date、regexp、map、set 及嵌套组合,并安全应对循环引用。











