深拷贝

33 阅读1分钟

面试中优先考虑写这个:

function deepClone(obj) {
  // 基础数据类型,直接返回
  if (obj === null || typeof obj !== "object") {
    return obj;
  }
  // 引用类型,进一步区分数组和对象
  let target = Array.isArray(obj) ? [] : {};
  // 使用for...in循环,递归拷贝
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      target[key] = deepClone(obj[key]);
    }
  }
  return target;
}

如果要求处理循环引用的,可以这么做:

function deepClone(obj, hash = new WeakMap()) {
  if (obj === null || typeof obj !== "object") {
    return obj;
  }
  // 出现了循环引用,则返回原对象
  if (hash.has(obj)) {
    return hash.get(obj);
  }

  let target = Array.isArray(obj) ? [] : {};
  // 哈希表记录出现过的对象
  hash.set(obj, target);

  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      // 从始至终只有初始创建的一个WeakMap对象(哈希表)
      target[key] = deepClone(obj[key], hash);
    }
  }
  return target;
}

这里使用 WeakMap 而不是 Map 是为了避免内存泄漏。

日常开发

对于结构相对简单的纯数据对象(不包含函数、Date等特殊类型),且没有循环引用时,直接使用:

let target = JSON.parse(JSON.stringify(obj));

对象包含特殊类型对象,最全面的深拷贝,项目中直接使用lodash库提供的cloneDeep

import _ from 'lodash;
let target = _.cloneDeep(obj);