JavaScript 深拷贝实现方法大全
深拷贝是指创建一个新对象,并递归复制原对象的所有属性,使得新对象与原对象完全独立,修改新对象不会影响原对象。以下是多种深拷贝的实现方式:
一、JSON 方法(最简单但有局限)
function deepCopy(obj) { return JSON.parse(JSON.stringify(obj)); }
特点:
-
✅ 简单快捷
-
❌ 不能复制函数、Symbol、undefined
-
❌ 不能处理循环引用
-
❌ 会丢失对象的原型链
适用场景:简单的数据对象拷贝
二、递归实现(基础版)
function deepCopy(obj) { if (obj === null || typeof obj !== 'object') { return obj; } let copy = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { copy[key] = deepCopy(obj[key]); } } return copy; }
三、递归实现(增强版)
处理更多边界情况:
function deepCopy(obj, hash = new WeakMap()) { // 处理基本类型和null if (obj === null || typeof obj !== 'object') return obj; // 处理循环引用 if (hash.has(obj)) return hash.get(obj); // 处理Date if (obj instanceof Date) return new Date(obj); // 处理RegExp if (obj instanceof RegExp) return new RegExp(obj); // 处理Set if (obj instanceof Set) { const copy = new Set(); hash.set(obj, copy); obj.forEach(value => copy.add(deepCopy(value, hash))); return copy; } // 处理Map if (obj instanceof Map) { const copy = new Map(); hash.set(obj, copy); obj.forEach((value, key) => copy.set(key, deepCopy(value, hash))); return copy; } // 处理数组或普通对象 const copy = Array.isArray(obj) ? [] : Object.create( Object.getPrototypeOf(obj) ); hash.set(obj, copy); // 处理Symbol属性 const symKeys = Object.getOwnPropertySymbols(obj); for (const symKey of symKeys) { copy[symKey] = deepCopy(obj[symKey], hash); } // 处理普通属性 for (const key in obj) { if (obj.hasOwnProperty(key)) { copy[key] = deepCopy(obj[key], hash); } } return copy; }
四、使用第三方库
1. lodash 的 _.cloneDeep
import _ from 'lodash'; const copied = _.cloneDeep(original);
2. jQuery 的 $.extend
const copied = $.extend(true, {}, original);
五、MessageChannel(异步深拷贝)
function deepCopy(obj) { return new Promise(resolve => { const { port1, port2 } = new MessageChannel(); port2.onmessage = ev => resolve(ev.data); port1.postMessage(obj); }); } // 使用 const copied = await deepCopy(original);
特点:
-
✅ 可以处理循环引用
-
❌ 异步操作
-
❌ 不能复制函数
六、structuredClone API(现代浏览器)
const copied = structuredClone(original);
特点:
-
✅ 浏览器原生方法
-
✅ 处理循环引用
-
❌ 不能复制函数、DOM节点等
-
❌ IE不支持
七、性能对比
方法 | 函数 | Symbol | 循环引用 | 性能 |
---|---|---|---|---|
JSON | ❌ | ❌ | ❌ | ★★★★ |
递归 | ✅ | ✅ | ✅ | ★★ |
lodash | ✅ | ✅ | ✅ | ★★★ |
MessageChannel | ❌ | ❌ | ✅ | ★★ |
structuredClone | ❌ | ✅ | ✅ | ★★★★ |
八、实际应用建议
-
简单数据对象:使用
JSON.parse(JSON.stringify())
-
需要完整拷贝:使用 lodash 的
_.cloneDeep
-
现代浏览器环境:尝试
structuredClone
-
特殊需求:根据需求选择或自定义递归实现
九、循环引用处理示例
const obj = { a: 1 }; obj.self = obj; // 循环引用 // 使用WeakMap处理循环引用的深拷贝 function deepCopy(obj, hash = new WeakMap()) { if (hash.has(obj)) return hash.get(obj); let copy = Array.isArray(obj) ? [] : {}; hash.set(obj, copy); for (let key in obj) { if (obj.hasOwnProperty(key)) { copy[key] = deepCopy(obj[key], hash); } } return copy; } const copied = deepCopy(obj); console.log(copied.self === copied); // true
选择深拷贝方法时,应根据实际需求考虑数据类型、性能要求和运行环境等因素。