Skip to main content

深拷贝

要求

  • 拷贝原始类型
  • 支持对象、数组、日期、正则的拷贝
  • 处理 Symbol 作为键名的情况
  • 解决循环引用递归爆栈问题

深拷贝

  • JSON.parse(JSON.stringify())。无法拷贝部分数据类型,也无法解决循环引用的问题
  • 递归。可能会爆栈
  • 循环

递归

需要注意下 Symbol 的情况,而且递归层次太多会爆栈。至于对象循环引用的问题,就只能通过返回已有的对象引用来解决了,不论是用递归还是循环的方式

启蒙版。后面其他版本都是在这个基础上做一些优化

function deepClone(obj) {
if (obj === null || typeof obj !== "object") {
return obj;
}
let clone = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key]);
}
}
return clone;
}
function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (typeof obj !== "object") return obj;
// 解决循环引用
if (hash.get(obj)) return hash.get(obj);
// 拷贝 Symbol 值
let symKeys = Object.getOwnPropertySymbols(obj);
if (symKeys.length) {
symKeys.forEach((symKey) => {
if (typeof obj[symKey] === "object") {
target[symKey] = deepClone(obj[symKey], hash);
} else {
target[symKey] = obj[symKey];
}
});
}
// 新建空数组或对象
let cloneObj = new obj.constructor();
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}

const obj0 = { name: "Dylan", info: { age: 24, hobby: ["看电影", "打游戏"] } };
const obj1 = deepClone(obj0);
console.log("obj0", obj0);
console.log("obj1", obj1);
obj1.name = "Jack";
console.log("obj1 update name");
console.log("obj0", obj0);
console.log("obj1", obj1);

循环

借助栈和广度优先策略来实现,同样要解决循环引用的问题

function deepCloneByLoop(data) {
if (!isObject(data)) return data;
// 解决循环引用
const visitedMap = new Map();
// 实现BFS的辅助队列
const queue = [];
queue.push(data);
while (queue.length) {
let curData = queue.shift();
if (visitedMap.get(curData)) {
let obj = visitedMap.get(curData);
} else {
let obj = new data.constructor();
visitedMap.set(curData, obj);
}

let keys = Object.keys(curData);
for (let i = 0, len = keys.length; i < len; i++) {
let temp = curData[keys[i]];
if (!isObject(temp)) {
obj[keys[i]] = temp;
}
if (visitedMap.get(temp)) {
obj[keys[i]] = visitedMap.get(temp);
} else {
obj[keys[i]] = Array.isArray(curData) ? [] : {};
visitedMap.set(temp, obj[keys[i]]);
queue.push(temp);
}
}
}
return visitedMap.get(data);
}

const obj0 = { name: "Dylan", info: { age: 24, hobby: ["看电影", "打游戏"] } };
const obj1 = deepCloneByLoop(obj0);
console.log("obj0", obj0);
console.log("obj1", obj1);
obj1.name = "Jack";
console.log("obj1 update name");
console.log("obj0", obj0);
console.log("obj1", obj1);

浅拷贝

  • 展开运算符 ...

对象

  • Object.assign

数组

  • slice
  • concat

参考