JS手写自己的深拷贝函数

48 阅读2分钟
1. 准备一个包含多种数据类型的对象
let obj1 = {
  arr: [1, 2, 3],
  arrayOfObjs: [{ c: 5 }, { d: 6 }],
  object: { val: 4 },
  // 增加一个属性
  fn: function () {
    return 5;
  },
  map: new Map(),
  set: new Set(),
  date: new Date(),
  reg: /aa/,
  nul: null,
  undef: undefined
};

// 环形对象
var obj2 = {
  to: obj1
};
obj1.to = obj2;
2. 手写自己的深拷贝函数
  • 定义自己的深拷贝函数
function deepClone(obj) {

}

思路点:

  • 基本数据类型 undefined null function统一进行处理
if (typeof obj !== 'object' || !obj) {
  return obj;
}
  • 非对象和非数组的数据都要单独处理,比如Map、Set、RegExp等
let tmp;
// 处理非对象和数组
if (obj instanceof Map) {
  tmp = new Map();
  cache.set(obj, tmp);
  obj.forEach((val, key) => {
    tmp.set(deepClone(key), deepClone(val))
  })
} else if (obj instanceof Set) {
  tmp = new Set();
  cache.set(obj, tmp);
  obj.forEach(val => {
    tmp.add(deepClone(val))
  })
} else if (obj instanceof RegExp || obj instanceof Date) {
  tmp = new obj.constructor(obj);
  cache.set(obj, tmp);
} else {
  tmp = new obj.constructor();
  cache.set(obj, tmp);
  for (let key in obj) {
    tmp[key] = deepClone(obj[key]);
  }
}
  • 单独处理环形对象,避免带来其的递归栈内存溢出
  • 新增Map数据类型来存放要处理的对象,为了避免强引用使用WeakMap定义
  • 并增加是否包含的判断,对于已经处理过的数据,就不再进行递归(针对环形数据进行的处理)
let cache = new WeakMap(); // 避免强引用
// 每种数据处理逻辑里新增代码
cache.set(obj,tmp);
//并在每次递归进来,处理完基本数据类型后面新增判断是否处理过的代码
if (cache.has(obj)) {
  return cache.get(obj);
}
3. 完整代码
// 处理环形对象带来的递归栈内存溢出
let cache = new WeakMap(); // 避免强引用
let cloned = deepClone(obj1);

function deepClone(obj) {
  // 处理了基本数据类型  undefined null function
  if (typeof obj !== 'object' || !obj) {
    return obj;
  }
  if (cache.has(obj)) {
    return cache.get(obj);
  }
  let tmp;
  // 处理非对象和数组
  if (obj instanceof Map) {
    tmp = new Map();
    cache.set(obj, tmp);
    obj.forEach((val, key) => {
      tmp.set(deepClone(key), deepClone(val))
    })
  } else if (obj instanceof Set) {
    tmp = new Set();
    cache.set(obj, tmp);
    obj.forEach(val => {
      tmp.add(deepClone(val))
    })
  } else if (obj instanceof RegExp || obj instanceof Date) {
    tmp = new obj.constructor(obj);
    cache.set(obj, tmp);
  } else {
    tmp = new obj.constructor();
    cache.set(obj, tmp);
    for (let key in obj) {
      tmp[key] = deepClone(obj[key]);
    }
  }
  return tmp;
}