文章目录
- 一、深拷贝实现代码
- 二、代码讲解
- 2.1 obj.constructor(obj)
- 2.2 防止循环引用
手写一个深拷贝是我们常见的面试题,在实现过程中我们需要考虑的类型很多,包括对象、数组、函数、日期等。以下就是深拷贝实现逻辑
一、深拷贝实现代码
const originalObject = {
string: 'Hello',
number: 42,
boolean: true,
array: [1, 2, 3],
nestedObject: { key: 'value' },
date: new Date(),
regex: /pattern/g,
map: new Map([['key1', 'value1'], ['key2', 'value2']]),
set: new Set([1, 2, 3]),
func: function (a, b) { return a + b; },
symbol: Symbol('symbol')
};
function deepClone(obj,visited = new WeakMap()){
if (visited.has(obj)) {
// 防止循环引用
return visited.get(obj);
}
if(typeof obj !== 'object' || obj === null){
// 处理基本数据类型和null
return obj
}
if(obj instanceof Date || obj instanceof RegExp || obj instanceof Map || obj instanceof Set){
//处理特殊对象
return new obj.constructor(obj)
}
if(Array.isArray(obj) || obj instanceof Object){
//处理数组和对象
const cloneObj = new obj.constructor();
visited.set(obj, cloneObj);
for (let key in obj) {
cloneObj[key] = deepClone(obj[key],visited)
}
return cloneObj
}
}
const clonedObject = deepClone( originalObject);
console.log(clonedObject);
console.log(originalObject)
二、代码讲解
在这里我讲解代码中我认为比较难懂的点,若大家还有什么其它不懂的地方,欢迎留言评论
2.1 obj.constructor(obj)
obj.constructor(obj)
这种写法通常用于创建一个对象的副本。针对于map,set,date,regex可以再创建一个相同但内存地址不同的的对象
const originalObject= new Set([1, 2, 3])
const cloneObj = new originalObject.constructor(originalObject);
cloneObj.add(4)
console.log(originalObject);
console.log(cloneObj)
但对于构造函数,对象,会再创建一个相同的对象(地址也相同),所以上面对于数组对象的处理我们并没有传参。
const originalObject= {
a:1,
b:2
}
const cloneObj = new originalObject.constructor();
cloneObj.c= 3
console.log(originalObject);//{a: 1, b: 2, c: 3}
console.log(cloneObj) //{a: 1, b: 2, c: 3}
2.2 防止循环引用
在上面提供的深拷贝实现中,防止循环引用的处理主要通过使用 WeakMap 实现。WeakMap 是 ECMAScript 6 引入的一种数据结构,它允许将对象作为键存储在 Map 中,但不会阻止这些对象被垃圾回收。这使得 WeakMap 非常适合用于处理循环引用的情况。
- 创建 visited WeakMap:参数visited = new WeakMap(),用于存储已经访问过的对象
function deepClone(obj, visited = new WeakMap()) {
// ...
}
- 检查是否已访问过:在处理每个对象之前,通过 visited.has(obj) 检查对象是否已经被访问过。
if (visited.has(obj)) {
return visited.get(obj);
}
- 将对象存储到 visited 中:在处理对象时,将其存储到 visited 中,以便后续检查循环引用。
visited.set(obj, cloneObj);
通过上面这样的处理,可以确保在深度拷贝对象的过程中,对于已经访问过的对象,直接返回其拷贝,而不会陷入无限递归的循环引用中