使用結構化克隆在 JavaScript 中進行深度複製

四六十發表於2022-01-13

在很長一段時間內,您不得不求助於變通方法和庫來建立 JavaScript 值的深層副本。現在js提供 structuredClone() 一個用於深度複製的內建函式。

瀏覽器支援:

淺拷貝

在 JavaScript 中複製一個值幾乎是淺拷貝,而不是深拷貝。這意味著對深度巢狀值的更改將在副本和原始值中可見。
使用物件擴充套件運算子 在 JavaScript 中建立淺拷貝的一種方法

const myOriginal = {
  someProp: "with a string value",
  anotherProp: {
    withAnotherProp: 1,
    andAnotherProp: true
  }
};

const myShallowCopy = {...myOriginal};

直接在淺副本上新增或更改屬性只會影響副本,而不影響原始值:

myShallowCopy.aNewProp = "a new value";
console.log(myOriginal.aNewProp)
// ^ logs `undefined`

然而,新增或更改巢狀很深的屬性會影響原始值和拷貝值:

myShallowCopy.anotherProp.aNewProp = "a new value";
console.log(myOriginal.anotherProp.aNewProp) 
// ^ logs `a new value`

物件擴充套件運算子遍歷了myOriginal所有可列舉的屬性。它使用屬性名稱和值,並將它們一一分配給一個新建立的空物件。因此,生成的物件在形狀上是相同的,但具有自己的屬性和值列表的副本。這些值也會被複制,但是 JavaScript 值處理所謂的基本型別的方式與處理非基本型別的方式不同。
非基本型別作為引用處理,這意味著複製值的行為實際上只是複製對同一底層物件的引用,從而導致淺複製行為。

深拷貝

與淺拷貝相反的是深拷貝。深拷貝演算法也一個一個地拷貝一個物件的屬性,但是當它找到對另一個物件的引用時遞迴地呼叫自己,同時建立該物件的副本。這對於確保兩段程式碼不會意外共享一個物件並在不知不覺中操縱彼此的狀態非常重要。
在 JavaScript 中建立值的深層副本過去沒有簡單或好的方法。很多人依賴第三方庫,比如Lodash 的cloneDeep()函式。可以說,這個問題最常見的解決方案是基於 JSON 的 hack:

const myDeepCopy = JSON.parse(JSON.stringify(myOriginal));

事實上,這是一個非常流行的解決方法,V8 積極優化 JSON.parse(),特別是上面的模式,以使其儘可能快。雖然速度很快,但它也有一些缺點:

  • 遞迴資料結構:JSON.stringify()當你序列化一個遞迴資料結構時會報錯。在使用連結串列或樹時,這很容易發生。
  • 內建型別:JSON.stringify()如果值包含其他JS關鍵字,例如Map,Set,Date,RegExp或ArrayBuffer,也會報錯。
  • Functions:JSON.stringify()有可能會丟掉函式。

結構化克隆

瀏覽器已經需要在幾個地方建立 JavaScript 值的深層副本的能力:在 IndexedDB 中儲存 JS 值需要某種形式的序列化,以便可以將其儲存在磁碟上,然後反序列化以恢復 JS 值。類似地,向 WebWorker 傳送訊息postMessage()需要將 JS 值從一個 JS 領域傳輸到另一個領域。
HTML 規範進行了修改,以公開一個名為的函式structuredClone(),該函式完全執行該演算法,作為開發人員輕鬆建立 JavaScript 值的深層副本的一種手段。

const myDeepCopy = structuredClone(myOriginal);

特點和限制

結構化克隆解決了該JSON.stringify()技術的許多(儘管不是全部)缺點。結構化克隆可以處理迴圈資料結構,支援許多內建資料型別,並且通常更健壯且通常更快。
但是,它仍然有一些限制可能會讓您措手不及:

  • Prototypes:如果structuredClone()與類例項一起使用,您將獲得一個普通物件作為返回值,因為結構化克隆會丟棄物件的原型鏈。
  • 函式:同樣不支援函式。
  • Non-cloneables:一些值不是結構化可克隆的,最值得注意的是Error和 DOM 節點。它會導致structuredClone()丟擲異常。

如果這些限制中的任何一個對您的用例造成破壞,像 Lodash 這樣的庫仍然提供其他深度克隆演算法的自定義實現,這些演算法可能適合您的用例,也可能不適合您的用例。

相關文章