深拷贝
要求
- 拷贝原始类型
- 支持对象、数组、日期、正则的拷贝
- 处理 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