Mike

13 Nov, 2024

JavaScript 處理深拷貝的幾種方法!

在撰寫 JS 的時候我們很常會對物件去進行深拷貝的處理,我們常見的深拷貝有三種做法,除了常見的做法以外,我還會額外介紹一個全新處裡深拷貝的方法。

關於淺拷貝跟深拷貝網路上已經有許多介紹文章了,這邊暫時不多做贅述!

  1. 使用JSON.parse(JSON.stringify(obj)):
    const obj2 = JSON.parse(JSON.stringify(obj1));

     

    這是最簡單的深拷貝方法,但所有非JSON支持的類型都將被忽略或轉換,日期會被轉換為字串,正規表達式會被丟棄。但是它的整體效能快,非常適合使用在小型且簡單的物件身上。

  2. 使用lodashcloneDeep

    身為前端開發中的老牌子,第三方資料處理的套件, lodash 可以說是耳熟能詳,它本身提供了許多方便的函式來幫我們處理跟資料有關的計算。

    它提供了cloneDeep函數,能夠更全面的深拷貝各種資料類型。

    import _ from 'lodash';
    const newObj = _.cloneDeep(obj1);

     

  3. 透過遞迴的方式來撰寫深拷貝的函式
    function deepCopy(obj, hash = new WeakMap()) {
      if (obj === null) return null;
      if (obj instanceof Date) return new Date(obj);
      if (obj instanceof RegExp) return new RegExp(obj);
      if (typeof obj !== 'object') return obj;
      if (hash.has(obj)) return hash.get(obj);
    
      const cloneObj = new obj.constructor();
      hash.set(obj, cloneObj);
    
      for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
          cloneObj[key] = deepCopy(obj[key], hash);
        }
      }
      return cloneObj;
    }
    const obj1 = {
      name: 'mike',
      info: {
        age: 12,
        address: 'taiwan',
        sex: 'boy',
        interest: {
          code: 'javascript',
          sports: 'basketball',
          watch: 'movie',
        },
      },
    };
    
    const obj2 = deepCopy(obj1);
    obj2.info.interest.code = "php";
    
    console.log(obj1.info.interest);
    console.log(obj2.info.interest);
    可以通過遞迴的方式來實現深拷貝,這給了我們在開發上面的靈活性,不過這種方式在開發的管理上面就顯得有點笨重。

    接下來我要介紹第四種方法,也是重頭戲

  4. 使用原生的 structuredClone 來進行深拷貝

    structuredClone 是 2022 年出來的新的 API,專門處理深拷貝的。在以前,我們常用JSON.parse(JSON.stringify(obj))來做深拷貝,但這個方法有很多問題。它不能複製函數、忽略undefined,也不能處理循環參考。而structuredClone就像是一個升級版,它能搞定這些情況,而且使用起來超級簡單。
    const obj2 = structuredClone(obj1);
    

    就這樣,二者完全獨立,互不影響,它真的很方便,尤其是在處理那些複雜的物件時。

    但 structuredClone 也不是完美的,它也有一些限制

    1. 函數無法複製:就像大多數的深拷貝方法一樣,structuredClone 不能複製函數。
    2. 某些物件型別不支持:雖然 structuredClone 支持許多內建的JavaScript type,但它並不支持複製某些特殊類型的物件,比如 Error 、Function等。
    3. 瀏覽器支援度:雖然現代瀏覽器大多支持 structuredClone,但在一些舊版本的瀏覽器中可能不支援。如果你的網站支援較舊瀏覽器,可能需要考慮其他替代方案。
    4. 轉換特殊類型時的行為差異:對於一些特殊類型的對象,比如 MapSetArrayBufferDataView 等,structuredClone 會進行深拷貝,但它的行為可能與一些自定義的深拷貝函數有所不同。

    這邊附上 structuredClone 一些相關參考跟介紹,有興趣的朋友可以看一下,可以學到蠻多的。

    Window:structuredClone() 方法

    结构化克隆算法

    使用 StructuredClone 在 JavaScript 中進行深度複製

    我自己觀察大部分有點資深的工程師比較少會知道 structuredClone 這個 API,畢竟在以前就有好幾種的方式可以處理深拷貝,所以第一時間反應不會去注意到說現在有新的 API 可以幫我們處理深拷貝。