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