🎯 一、为什么要掌握深拷贝?
- JS 中对象赋值是引用赋值,不是值复制
JSON.parse(JSON.stringify()) 有诸多缺陷(如丢失函数、正则、undefined、循环引用会报错)
- 项目中常需要完整复制一份对象结构,避免原始数据被意外修改
- 面试中常考实现 + 处理边界
📌 二、深拷贝 vs 浅拷贝 区别
| 拷贝类型 | 拷贝层级 | 会复制引用吗 | 工具 |
|---|
| 浅拷贝 | 只拷贝第一层 | 是 | Object.assign、展开运算符 |
| 深拷贝 | 全部层级递归 | 否(新地址) | 自定义函数、structuredClone(现代浏览器) |
🧠 三、深拷贝核心难点
- 递归嵌套结构
- 处理循环引用
- 正确处理各种类型(Date、RegExp、Set、Map、Function、Symbol、BigInt、null、undefined)
✍️ 四、基础版实现(不支持循环引用)
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj
const result = Array.isArray(obj) ? [] : {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key])
}
}
return result
}
🔁 五、进阶版:支持循环引用 & 多类型处理
function deepClone(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (hash.has(obj)) return hash.get(obj);
const type = Object.prototype.toString.call(obj);
if (type === '[object Date]') return new Date(obj);
if (type === '[object RegExp]') return new RegExp(obj.source, obj.flags);
if (type === '[object Map]') {
const map = new Map();
hash.set(obj, map);
obj.forEach((v, k) => map.set(deepClone(k, hash), deepClone(v, hash)));
return map;
}
if (type === '[object Set]') {
const set = new Set();
hash.set(obj, set);
obj.forEach(v => set.add(deepClone(v, hash)));
return set;
}
const result = Array.isArray(obj) ? [] : {};
hash.set(obj, result);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key], hash);
}
}
return result;
}
✅ 六、测试用例
6.1 基础对象
const obj = { name: 'Mark', age: 18, skills: ['JS', 'Vue'] }
const clone = deepClone(obj)
console.log(clone)
6.2 循环引用
const obj = { name: 'A' }
obj.self = obj
const clone = deepClone(obj)
console.log(clone.self === clone)
6.3 包含 Map/Set/Date/RegExp
const obj = {
date: new Date(),
reg: /abc/gi,
map: new Map([['a', 1]]),
set: new Set([1, 2])
};
const clone = deepClone(obj);
console.log(clone);
❗ 七、面试常见加分点
| 问题 | 解法 |
|---|
| 如何处理循环引用? | 使用 WeakMap |
| 为什么不用 JSON 序列化? | 会丢失函数、undefined、循环引用报错 |
| 如何处理特殊对象? | 判断 Object.prototype.toString.call(obj) 的类型 |
📘 八、现代浏览器方案
const newObj = structuredClone(obj)
缺点:
- 不支持 function、DOM 节点等
- 兼容性较差(IE、老版本 Safari)