手寫深拷貝

手寫深拷貝

深拷貝

深拷貝簡(jiǎn)單理解就是b是a的一份拷貝再榄,且b中不存在a中對(duì)象的引用

深拷貝的實(shí)現(xiàn)

1.JSON序列化和反序列化

如果對(duì)象中全是基本類型狡刘,那么可以使用JSON.parse(JSON.stringify(a))

2.遞歸克隆

2.1 如果是普通類型,就直接拷貝

測(cè)試用例:

describe('deepClone', () => {
  it('能夠復(fù)制基本類型number,string,boolean,undefined,null,symbol', () => {
    const n = 123;
    const n2 = deepClone(n);
    assert(n === n2);
    const s = '12345';
    const s2 = deepClone(s);
    assert(s === s2);
    const b = true;
    const b2 = deepClone(b);
    assert(b === b2);
    const u = undefined;
    const u2 = deepClone(u);
    assert(u === u2);
    const empty = null;
    const empty2 = deepClone(empty);
    assert(empty === empty2);
    const sym = Symbol();
    const sym2 = deepClone(sym);
    assert(sym === sym2);
  });
});

代碼實(shí)現(xiàn):

function deepClone(source) {
  return source;
}
image.png
image.png

此時(shí)我們能夠?qū)崿F(xiàn)基本類型的拷貝

2.2 如果是對(duì)象

2.2.1 普通對(duì)象

使用for in 遍歷對(duì)象上的屬性不跟,返回一個(gè)新的對(duì)象(注: for in 會(huì)遍歷原型上的屬性)
測(cè)試用例:

it('能夠復(fù)制對(duì)象', () => {
      const obj1 = { name: 'sss', child: { name: 'sss-children' } };
      const obj2 = deepClone(obj1);
      assert(obj1 !== obj2);
      assert(obj1.name === obj2.name);
      assert(obj1.child !== obj2.child);
      assert(obj1.child.name === obj2.child.name);
    });

代碼實(shí)現(xiàn):

function deepClone(source) {
  if (source instanceof Object) {
    let dist = new Object();
    for (let key in source) {
      // for in會(huì)遍歷原型上的屬性
      if (source.hasOwnProperty(key)) {
        dist[key] = deepClone(source[key]);
      }
    }

    return dist;
  }

  return source;
}
image.png
image.png
2.2.2 數(shù)組對(duì)象

使用new Array初始化

測(cè)試用例:

it('能夠復(fù)制數(shù)組對(duì)象', () => {
      const array1 = [
        [11, 12],
        [21, 22],
        [31, 32],
      ];
      const array2 = deepClone(array1);
      assert(array1 !== array2);
      assert(array1[0] !== array2[0]);
      assert(array1[1] !== array2[1]);
      assert(array1[2] !== array2[2]);
      assert.deepEqual(array1, array2);
    });

代碼實(shí)現(xiàn):

function deepClone(source) {
  if (source instanceof Object) {
    let dist;
    if (source instanceof Array) {
      dist = new Array();
    } else {
      dist = new Object();
    }

    for (let key in source) {
      // for in會(huì)遍歷原型上的屬性
      if (source.hasOwnProperty(key)) {
        dist[key] = deepClone(source[key]);
      }
    }

    return dist;
  }

  return source;
}
2.2.3 函數(shù)對(duì)象

返回一個(gè)新的函數(shù),使用apply(this,arguments)

測(cè)試用例:

 it('能夠復(fù)制函數(shù)', () => {
      const f1 = function (x, y) {
        return x + y;
      };
      f1.xxx = { yyy: { zzz: 1 } };
      const f2 = deepClone(f1);
      assert(f1 !== f2);
      assert(f1.xxx !== f2.xxx);
      assert(f1.xxx.yyy !== f2.xxx.yyy);
      assert(f1.xxx.yyy.zzz === f2.xxx.yyy.zzz);
      assert(f1(1, 2) === f2(1, 2));
    });

代碼實(shí)現(xiàn):

function deepClone(source) {
  if (source instanceof Object) {
    let dist;
    if (source instanceof Array) {
      dist = new Array();
    } else if (source instanceof Function) {
      dist = function () {
        return source.apply(this, arguments);
      };
    } else {
      dist = new Object();
    }
    for (let key in source) {
      // for in會(huì)遍歷原型上的屬性
      if (source.hasOwnProperty(key)) {
        dist[key] = deepClone(source[key]);
      }
    }

    return dist;
  }

  return source;
}
2.2.4 日期和正則

日期使用new Date創(chuàng)建一個(gè)新的日期對(duì)象颓帝,正則使用new RegExp(source.source, source.flags)

測(cè)試用例:

 it('可以復(fù)制正則', () => {
      const reg1 = /hi\d+/gi;
      reg1.xxx = { yyy: { zzz: 1 } };
      const reg2 = deepClone(reg1);
      assert(reg1.source === reg2.source);
      assert(reg1.flags === reg2.flags);
      assert(reg1 !== reg2);
      assert(reg1.xxx !== reg2.xxx);
      assert(reg1.xxx.yyy !== reg2.xxx.yyy);
      assert(reg1.xxx.yyy.zzz === reg2.xxx.yyy.zzz);
    });
    it('可以復(fù)制日期', () => {
      const date1 = new Date();
      date1.xxx = { yyy: { zzz: 1 } };
      const date2 = deepClone(date1);
      assert(date1.source === date2.source);
      assert(date1.flags === date2.flags);
      assert(date1 !== date2);
      assert(date1.getTime() === date2.getTime());
      assert(date1.xxx !== date2.xxx);
      assert(date1.xxx.yyy !== date2.xxx.yyy);
      assert(date1.xxx.yyy.zzz === date2.xxx.yyy.zzz);
    });

代碼實(shí)現(xiàn)

function deepClone(source) {
  if (source instanceof Object) {
    let dist;
    if (source instanceof Array) {
      dist = new Array();
    } else if (source instanceof Function) {
      dist = function () {
        return source.apply(this, arguments);
      };
    } else if (source instanceof RegExp) {
      dist = new RegExp(source.source, source.flags);
    } else if (source instanceof Date) {
      dist = new Date(source);
    } else {
      dist = new Object();
    }
    for (let key in source) {
      // for in會(huì)遍歷原型上的屬性
      if (source.hasOwnProperty(key)) {
        dist[key] = deepClone(source[key]);
      }
    }

    return dist;
  }

  return source;
}
image.png
image.png

目前我們粗略的實(shí)現(xiàn)了一個(gè)可以復(fù)制基本類型和常見(jiàn)對(duì)象的深拷貝

2.3 考慮環(huán)

const a = {}
a.self = a

測(cè)試用例:

it('環(huán)也能復(fù)制', () => {
      const obj1 = { name: 'sss' };
      obj1.self = obj1;
      const obj2 = deepClone(obj1);
      assert(obj1 !== obj2);
      assert(obj1.name === obj2.name);
      assert(obj1.self !== obj2.self);
    });

此時(shí)我們直接運(yùn)行之前的代碼,發(fā)現(xiàn)控制臺(tái)直接報(bào)錯(cuò)了。因?yàn)閷?duì)象每次深拷貝之后會(huì)生成一個(gè)全新的對(duì)象,所以此時(shí)會(huì)造成遞歸不能終止购城。因此我們需要先判斷是否存在環(huán)吕座,然后進(jìn)行對(duì)象的拷貝。

我們創(chuàng)建一個(gè)cashe數(shù)組瘪板,里面存放origin和new

let cache = [];

function findCache(source) {
  for (let i = 0; i < cache.length; i++) {
    if (cache[i].origin && cache[i].origin === source) {
      return cache[i].new;
    }
  }
  return undefined;
}
function deepClone(source) {
  if (source instanceof Object) {
    let cachedDist = findCache(source);
    if (cachedDist) {
      return cachedDist;
    } else {
      let dist;
      if (source instanceof Array) {
        dist = new Array();
      } else if (source instanceof RegExp) {
        dist = new RegExp(source.source, source.flags);
      } else if (source instanceof Function) {
        dist = function () {
          return source.apply(this, arguments);
        };
      } else if (source instanceof Date) {
        dist = new Date(source);
      } else {
        dist = new Object();
      }
      cache.push({ origin: source, new: dist });
      for (let key in source) {
        // for in會(huì)遍歷原型上的屬性
        if (source.hasOwnProperty(key)) {
          dist[key] = deepClone(source[key]);
        }
      }

      return dist;
    }
  }
  return source;
}
image.png
image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吴趴,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子侮攀,更是在濱河造成了極大的恐慌锣枝,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兰英,死亡現(xiàn)場(chǎng)離奇詭異撇叁,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)畦贸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門陨闹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人薄坏,你說(shuō)我怎么就攤上這事趋厉。” “怎么了胶坠?”我有些...
    開(kāi)封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵君账,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我沈善,道長(zhǎng)乡数,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任矮瘟,我火速辦了婚禮瞳脓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘澈侠。我一直安慰自己劫侧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布哨啃。 她就那樣靜靜地躺著烧栋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拳球。 梳的紋絲不亂的頭發(fā)上审姓,一...
    開(kāi)封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音祝峻,去河邊找鬼魔吐。 笑死扎筒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的酬姆。 我是一名探鬼主播嗜桌,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼辞色!你這毒婦竟也來(lái)了骨宠?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤相满,失蹤者是張志新(化名)和其女友劉穎层亿,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體立美,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡匿又,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悯辙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片琳省。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖躲撰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情击费,我是刑警寧澤拢蛋,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站蔫巩,受9級(jí)特大地震影響谆棱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜圆仔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一垃瞧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坪郭,春花似錦个从、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至沪曙,卻和暖如春奕污,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背液走。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工碳默, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贾陷,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓嘱根,卻偏偏與公主長(zhǎng)得像髓废,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子儿子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容