文章目录
- 一、深拷贝实现代码
- 二、代码讲解
- 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){// 处理基本数据类型和nullreturn 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);
通过上面这样的处理,可以确保在深度拷贝对象的过程中,对于已经访问过的对象,直接返回其拷贝,而不会陷入无限递归的循环引用中