javascript深拷贝通过类型识别+分类构造+递归下沉统一处理对象数组:基础类型直接返回,date/regexp/new map/set等按需构造,数组与对象递归复制,用weakmap解决循环引用,并完整保留symbol属性和原型链。

JavaScript 中深拷贝处理对象数组,本质是把“数组”和“对象”两种引用类型都纳入统一的递归遍历逻辑——不是分别写两套代码,而是靠类型识别+分类构造+递归下沉来统一应对。
类型识别决定复制策略
深拷贝第一步不是急着复制,而是精准判断当前值的类型:
- 基础类型(string/number/boolean/null/undefined/Symbol):直接返回,不递归
-
Date、RegExp:用
new Date(obj)或new RegExp(obj)构造新实例 -
Array:创建空数组
[],再逐项递归拷贝元素 -
普通对象(包括 {}、Map、Set 等):区分构造方式——
Object.create(Object.getPrototypeOf(obj))保原型;new Map()或new Set()单独初始化并填充
数组元素可能混杂,递归必须穿透每一层
对象数组如 [{a: 1, b: [2, {c: true}]}, null, new Date()],递归过程会逐层展开:
- 外层数组 → 判断为 Array → 创建新数组
- 第 0 项
{a: 1, b: [2, {c: true}]}→ 判断为 Object → 创建新对象 → 递归处理b属性 -
b是数组 → 再创建新数组 → 继续递归其中的{c: true} - 第 1 项
null→ 直接返回 - 第 2 项
new Date()→ 用new Date(obj)实例化
循环引用靠 WeakMap 拦截,避免死递归
如果数组里包含重复引用或自引用(比如 let arr = [{x: 1}]; arr[0].ref = arr;),不做防护会栈溢出。正确做法是:
- 递归函数接收一个可选的
hash = new WeakMap()参数 - 每次进入引用类型前,先查
hash.has(obj),存在则直接返回缓存结果 - 首次处理时,立即
hash.set(obj, clone),把原始对象和它的克隆体绑定
Symbol 属性和原型链不能漏
只遍历 for...in 或 Object.keys() 会丢掉 Symbol 键和不可枚举属性:
- 用
Object.getOwnPropertySymbols(obj)获取所有 Symbol 键,逐一递归赋值 - 用
Object.getPrototypeOf(obj)获取原型,通过Object.create(proto)复制原型链 - 避免用
{}简单初始化,否则新对象丢失 constructor、方法继承等行为
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











