在 JavaScript 中深度克隆物件

Harshit Jindal 2023年10月12日
  1. 淺拷貝與深拷貝
  2. JavaScript 中的淺拷貝方法
  3. JavaScript 中的深拷貝方法
在 JavaScript 中深度克隆物件

JavaScript 是一種物件語言。幾乎所有內容都是 JavaScript 中的物件。布林值、數字、字串、日期、數學、Regex、陣列、函式和物件本身都是物件。它們是由各種屬性和方法組成的鍵值對的集合。它們直接儲存在記憶體中,並且只能通過引用進行復制。變數不儲存物件,而只是在記憶體中對該物件的引用。因此,當我們嘗試複製物件變數時,最終會建立對同一物件的額外引用。此方法稱為淺拷貝。這是不理想的,因為我們不希望更改原始物件來影響其克隆。這就需要一種方法來深度克隆物件。本教程講授如何在 JavaScript 中深度克隆物件。

淺拷貝與深拷貝

淺拷貝是物件的按位拷貝。建立的新物件成功複製了諸如數字、布林值和字串等基元,但未複製對物件的任何引用。只有引用地址會導致指向同一物件的指標。對原始物件所做的任何更改都會反映在淺拷貝中。

另一方面,深拷貝不僅將地址/引用複製到原始物件,還複製整個物件。建立的新物件與複製的物件沒有任何依賴關係。JavaScript 為我們提供了各種內建方法來複制物件,但是淺拷貝是大多數方法中的預設行為。

JavaScript 中的淺拷貝方法

我們將簡要介紹淺拷貝方法,以使你瞭解深拷貝的一些錯誤方法。

在 JavaScript 中使用 Spread 語法來淺拷貝物件

我們可以通過建立一個新物件來克隆物件,然後使用 Spread 語法將物件內部的內容列舉為物件本身。這似乎是正確的方法,但它會建立資料的淺拷貝。

const obj = {
  a: 1,
  b: {c: 2}
}

const clone = {...obj};  // creates a shallow copy

obj.b.c = 5;
console.log(clone.b.c);  // outputs 5

在上面的程式碼中,我們使用 spread 語法建立物件的淺拷貝。然後,我們在原始物件中修改了引用物件的屬性之一,並表明在克隆的物件中該屬性已被修改。

在 JavaScript 中使用 Object.assign() 淺拷貝物件

object.assign() 方法將物件的淺拷貝分配給新的物件變數。它有兩個引數:targetsourcetarget 通常是一對空括號,用於表示要在其中複製的空物件。它是一個可選引數,但是傳遞它可以確保我們最終不會更改原始物件。第二個引數是要複製的物件。

const obj = {
  a: 1,
  b: {c: 2}
}

const clone = Object.assign({}, obj);  // creates a shallow copy

obj.b.c = 5;
console.log(clone.b.c);  // outputs 5

在上面的程式碼中,我們使用 Object.assign() 建立物件的淺拷貝。然後,我們在原始物件中修改了引用物件的屬性之一,並表明在克隆的物件中該屬性已被修改。

JavaScript 中的深拷貝方法

在 JavaScript 中使用 JSON.parse()JSON.stringify() 深度克隆物件

JSON.stringify() 用於將 JavaScript 物件轉換為 JSON 字串,JSON.parse() 用於將 JSON 字串轉換為 JavaScript 物件。我們可以將 JSON.parse()包裹在 JSON.stringify()周圍,以首先將 JavaScript 物件轉換為 JSON String,然後對其進行解析以獲得該物件的副本。

var user = {name: 'Harshit', age: 21, Profession: 'Software Engineer'};
let fakeDeepCopy = JSON.parse(JSON.stringify(user));

此方法建立深拷貝,但僅適用於沒有功能的物件。與其他引用物件一樣,它在任何迴圈依賴方面都有問題。複製物件中屬性的順序也可能與原始物件不同。因此,如果我們有一個簡單的物件而原始資料型別很少,那麼此方法是一個很好的技巧,但不建議在現實世界中使用。

在 JavaScript 中使用本機深度克隆深度克隆物件

我們可以使用 Node.js v8 模組的序列化演算法來深度克隆物件。儘管它僅限於某些內建資料型別,但它保留了克隆資料中的引用。它允許我們複製 JSON 方法不支援的幾個迴圈和遞迴結構。

const v8 = require('v8');

const structuredClone = obj => {
  return v8.deserialize(v8.serialize(obj));
};

我們反序列化然後序列化物件,就像 JSON 方法的 stringify 和 parse 一樣。但是它保留了迴圈依賴關係,並且稍微好一些。

在 JavaScript 中使用 Lodash 庫深度克隆物件

Lodash 庫具有淺拷貝和深拷貝的函式,即 cloneclonedeep。這是一個很棒的庫,它允許我們僅匯入所需的功能,而不匯入完整的庫。clonedeep 方法的工作方式是遞迴地複製值,然後保留所有物件繼承,從而建立物件的真實副本。

const lodashClonedeep = require('lodash.clonedeep');
let obj = {a: 1, b: {c: 2}} let deepClone = lodashClonedeep(obj);

在這裡,我們從 lodash 載入 clonedeep 函式,並使用它來深克隆物件。它是一個經過良好測試和維護的庫,但只能與 Node.js 一起使用,不能與 Vanilla JavaScript 一起使用。

在 JavaScript 中使用 jQuery extend() 方法深度克隆物件

我們可以使用 jQuery 的 .extend() 進行物件的淺拷貝和深拷貝。這是最可靠的深度克隆方法,不會丟失資料或破壞資料。它的主要功能是合併兩個或更多物件。但是也可以用於克隆物件。它帶有以下引數:[deep]targetobject1 ..... objectN

  1. [deep]:這是一個可選引數。它唯一允許的值為 true。如果在函式中傳遞它,則該函式將建立物件的深拷貝。否則,它將形成一個淺拷貝。
  2. target:要擴充套件的物件。它將接收所有合併的物件。
  3. object1, ..., objectN:這些是要合併/克隆到新物件中的物件。
let obj = {a: 1, b: {c: 2}} let shallowClone =
    $.extend({}, obj);                    // creates a shallow copy
let deepClone = $.extend(true, {}, obj);  // creates a deep copy

一個更明顯的解決方案可以是遍歷源物件的每個屬性,然後將它們複製到新物件中。上面討論的所有方法都與所有主要的瀏覽器相容。

作者: Harshit Jindal
Harshit Jindal avatar Harshit Jindal avatar

Harshit Jindal has done his Bachelors in Computer Science Engineering(2021) from DTU. He has always been a problem solver and now turned that into his profession. Currently working at M365 Cloud Security team(Torus) on Cloud Security Services and Datacenter Buildout Automation.

LinkedIn

相關文章 - JavaScript Object