【前端100问】Q95:模拟实现一个深拷贝,并考虑对象相互引用以及 Symbol 拷贝的情况

写在前面

此系列来源于开源项目:前端 100 问:能搞懂 80%的请把简历给我
为了备战 2021 春招
每天一题,督促自己
从多方面多角度总结答案,丰富知识
模拟实现一个深拷贝,并考虑对象相互引用以及 Symbol 拷贝的情况
简书整合地址:前端 100 问

正文回答

主要问题是

  1. symbol 作为 key,不会被遍历到,所以 stringify 和 parse 是不行的
  2. 有环引用,stringify 和 parse 也会报错

我们另外用 getOwnPropertySymbols 可以获取 symbol key 可以解决问题 1,用集合记忆曾经遍历过的对象可以解决问题 2。当然,还有很多数据类型要独立去拷贝。比如拷贝一个 RegExp,lodash 是最全的数据类型拷贝了,有空可以研究一下

function deepCopy(target, cache = new Set()) {
  if (typeof target !== "object" || cache.has(target)) {
    return target;
  }
  if (Array.isArray(target)) {
    target.map((t) => {
      cache.add(t);
      return t;
    });
  } else {
    return [
      ...Object.keys(target),
      ...Object.getOwnPropertySymbols(target),
    ].reduce(
      (res, key) => {
        cache.add(target[key]);
        res[key] = deepCopy(target[key], cache);
        return res;
      },
      target.constructor !== Object
        ? Object.create(target.constructor.prototype)
        : {}
    );
  }
}

另外,如果不考虑用 symbol 做 key,还有两种黑科技深拷贝,可以解决环引用的问题,比 stringify 和 parse 优雅强一些

function deepCopyByHistory(target) {
  const prev = history.state;
  history.replaceState(target, document.title);
  const res = history.state;
  history.replaceState(prev, document.title);
  return res;
}

async function deepCopyByMessageChannel(target) {
  return new Promise((resolve) => {
    const channel = new MessageChannel();
    channel.port2.onmessage = (ev) => resolve(ev.data);
    channel.port1.postMessage(target);
  }).then((data) => data);
}

无论哪种方法,它们都有一个共性:失去了继承关系,所以剩下的需要我们手动补上去了,故有 Object.create(target.constructor.prototype)的操作

方法二
const symbolName = Symbol();
const obj = {
  objNumber: new Number(1),
  number: 1,
  objString: new String("ss"),
  string: "stirng",
  objRegexp: new RegExp("\\w"),
  regexp: /w+/g,
  date: new Date(),
  function: function () {},
  array: [{ a: 1 }, 2],
  [symbolName]: 111,
};
obj.d = obj;

const isObject = (obj) =>
  obj !== null && (typeof obj === "object" || typeof obj === "function");
const isFunction = (obj) => typeof obj === "function";
function deepClone(obj, hash = new WeakMap()) {
  if (hash.get(obj)) {
    // 环处理
    return hash.get(obj);
  }
  if (!isObject(obj)) {
    return obj;
  }

  if (isFunction(obj)) {
    // function返回原引用
    return obj;
  }

  let cloneObj;

  const Constructor = obj.constructor;

  switch (Constructor) {
    case Boolean:
    case Date:
      return new Date(+obj);
    case Number:
    case String:
    case RegExp:
      return new Constructor(obj);
    default:
      cloneObj = new Constructor();
      hash.set(obj, cloneObj);
  }

  [
    ...Object.getOwnPropertyNames(obj),
    ...Object.getOwnPropertySymbols(obj),
  ].forEach((k) => {
    cloneObj[k] = deepClone(obj[k], hash);
  });
  return cloneObj;
}

const o = deepClone(obj);
console.log(o.objNumber === obj.objNumber);
console.log(o.number === obj.number);
console.log(o.objString === obj.objString);
console.log(o.string === obj.string);
console.log(o.objRegexp === obj.objRegexp);
console.log(o.regexp === obj.regexp);
console.log(o.date === obj.date);
console.log(o.function === obj.function);
console.log(o.array[0] === obj.array[0]);
console.log(o[symbolName] === obj[symbolName]);
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容