实现深拷贝简洁版本
手写一个简洁版的深拷贝函数,处理对象、数组等常见数据类型的深层复制
问题
在 JavaScript 中,直接赋值或浅拷贝只会复制对象的引用,修改新对象会影响原对象。深拷贝需要递归地复制对象的所有层级,创建一个完全独立的副本。本题要求实现一个简洁版的深拷贝函数,能够处理常见的数据类型。
解答
/**
* 深拷贝函数(简洁版)
* @param {*} obj - 需要拷贝的对象
* @param {WeakMap} hash - 用于存储已拷贝的对象,解决循环引用问题
* @returns {*} 拷贝后的新对象
*/
function deepClone(obj, hash = new WeakMap()) {
// 处理 null 或 undefined
if (obj === null || obj === undefined) {
return obj;
}
// 处理基本数据类型
if (typeof obj !== 'object') {
return obj;
}
// 处理日期对象
if (obj instanceof Date) {
return new Date(obj);
}
// 处理正则对象
if (obj instanceof RegExp) {
return new RegExp(obj);
}
// 处理循环引用
if (hash.has(obj)) {
return hash.get(obj);
}
// 根据原对象的构造函数创建新对象(保持原型链)
const cloneObj = new obj.constructor();
// 存储到 hash 中,用于处理循环引用
hash.set(obj, cloneObj);
// 递归拷贝对象的所有属性
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
使用示例
// 示例1:基本对象拷贝
const obj1 = {
name: '张三',
age: 25,
hobbies: ['读书', '旅游'],
address: {
city: '北京',
district: '朝阳区'
}
};
const clonedObj1 = deepClone(obj1);
clonedObj1.hobbies.push('游泳');
clonedObj1.address.city = '上海';
console.log(obj1.hobbies); // ['读书', '旅游']
console.log(obj1.address.city); // '北京'
console.log(clonedObj1.hobbies); // ['读书', '旅游', '游泳']
console.log(clonedObj1.address.city); // '上海'
// 示例2:处理循环引用
const obj2 = { name: '李四' };
obj2.self = obj2; // 循环引用
const clonedObj2 = deepClone(obj2);
console.log(clonedObj2.self === clonedObj2); // true
console.log(clonedObj2 === obj2); // false
// 示例3:处理特殊对象
const obj3 = {
date: new Date('2024-01-01'),
regex: /test/gi,
arr: [1, 2, { a: 3 }]
};
const clonedObj3 = deepClone(obj3);
console.log(clonedObj3.date instanceof Date); // true
console.log(clonedObj3.regex instanceof RegExp); // true
console.log(clonedObj3.arr[2] === obj3.arr[2]); // false
关键点
-
类型判断:首先判断是否为 null、undefined 或基本数据类型,这些情况直接返回原值
-
特殊对象处理:针对 Date、RegExp 等特殊对象,使用对应的构造函数创建新实例
-
循环引用处理:使用 WeakMap 存储已拷贝的对象,避免无限递归。WeakMap 的键是弱引用,不会阻止垃圾回收
-
保持原型链:使用
new obj.constructor()创建新对象,保持与原对象相同的原型链 -
递归拷贝:使用
for...in遍历对象的可枚举属性,配合hasOwnProperty过滤原型链上的属性,递归调用深拷贝函数 -
简洁性权衡:此版本未处理 Map、Set、Symbol 等复杂类型,如需完整版本可进一步扩展
目录