JavaScript 對象的複制並非表面看起來那麼簡單。理解此過程中對象和引用的工作方式對於 Web 開發人員至關重要,可以節省數小時的調試時間。當您使用大型有狀態應用程序(例如在 React 或 Vue 中構建的應用程序)時,這一點變得越來越重要。
淺複製和深複製指的是我們在 JavaScript 中如何創建對象的副本以及在“副本”中創建了哪些數據。在本文中,我們將深入探討這些方法之間的區別,探討它們的實際應用,並揭示使用它們時可能出現的潛在陷阱。
淺複製是指創建新對象的過程,該對像是現有對象的副本,其屬性引用與原始對象相同的數值或對象。在 JavaScript 中,這通常是使用 Object.assign()
或展開語法({...originalObject}
)等方法實現的。淺複製僅創建對現有對像或值的新的引用,不會創建深複製,這意味著嵌套對象仍然是引用的,而不是重複的。
讓我們看看下面的代碼示例。新創建的對象 shallowCopyZoo
是通過展開運算符創建的 zoo
的副本,這導致了一些意外的後果。
let zoo = { name: "Amazing Zoo", location: "Melbourne, Australia", animals: [ { species: "Lion", favoriteTreat: "?", }, { species: "Panda", favoriteTreat: "?", }, ], }; let shallowCopyZoo = { ...zoo }; shallowCopyZoo.animals[0].favoriteTreat = "?"; console.log(zoo.animals[0].favoriteTreat); // "?",而不是 "?"
但是讓我們看看 shallowCopyZoo
中究竟是什麼。屬性 name
和 location
是原始值(字符串),因此它們的數值被複製。但是,animals
屬性是對象的數組,因此復制的是對該數組的引用,而不是數組本身。
您可以使用嚴格相等運算符 (===
) 快速測試這一點(如果您不相信我)。只有當對象引用同一對象時,一個對象才等於另一個對象(參見原始數據類型與引用數據類型)。請注意,animals
屬性在兩者上是相等的,但對象本身並不相等。
let zoo = { name: "Amazing Zoo", location: "Melbourne, Australia", animals: [ { species: "Lion", favoriteTreat: "?", }, { species: "Panda", favoriteTreat: "?", }, ], }; let shallowCopyZoo = { ...zoo }; shallowCopyZoo.animals[0].favoriteTreat = "?"; console.log(zoo.animals[0].favoriteTreat); // "?",而不是 "?"
這可能導致代碼庫中出現潛在問題,並且在處理大型修改時尤其困難。修改淺副本中的嵌套對像也會影響原始對象和任何其他淺副本,因為它們都共享相同的引用。
深複製是一種創建新對象的技巧,該對像是現有對象的精確副本。這包括複製其所有屬性和任何嵌套對象,而不是引用。當您需要兩個不共享引用的獨立對象時,深克隆很有用,確保對一個對象的更改不會影響另一個對象。
程序員在處理複雜應用程序中的應用程序狀態對象時經常使用深克隆。創建新的狀態對象而不影響先前狀態對於維護應用程序的穩定性和正確實現撤消/重做功能至關重要。
JSON.stringify()
和 JSON.parse()
進行深複製一種流行且無需庫的深複製方法是使用內置的 JSON.stringify()
和 JSON.parse()
方法。
parse(stringify())
方法並不完美。例如,Date
等特殊數據類型將被轉換為字符串,未定義的值將被忽略。與本文中的所有選項一樣,應根據您的具體用例進行考慮。
在下面的代碼中,我們將使用這些方法創建一個 deepCopy
函數來深度克隆一個對象。然後,我們複製 playerProfile
對象並修改複製的對象,而不會影響原始對象。這展示了深複製在維護不共享引用的獨立對象方面的價值。
console.log(zoo.animals === shallowCopyZoo.animals) // true console.log(zoo === shallowCopyZoo) // false
還有一些各種第三方庫提供了深複製解決方案。
cloneDeep()
函數可以正確處理循環引用、函數和特殊對象。 extend()
[deep = true]
函數如果由於某種原因您不想使用 JSON 對像或第三方庫,您也可以在 Vanilla JavaScript 中創建一個自定義深複製函數。該函數遞歸迭代對象屬性並創建一個具有相同屬性和值的新對象。
const playerProfile = { name: 'Alice', level: 10, achievements: [ { title: 'Fast Learner', emoji: '?' }, { title: 'Treasure Hunter', emoji: '?' } ] }; function deepCopy(obj) { return JSON.parse(JSON.stringify(obj)); } const clonedProfile = deepCopy(playerProfile); console.log(clonedProfile); // 输出与playerProfile相同 // 修改克隆的配置文件而不影响原始配置文件 clonedProfile.achievements.push({ title: 'Marathon Runner', emoji: '?' }); console.log(playerProfile.achievements.length); // 输出:2 console.log(clonedProfile.achievements.length); // 输出:3
雖然深複製為數據準確性提供了極大的好處,但建議評估對於每個特定用例是否需要深複製。在某些情況下,淺複製或其他管理對象引用的技術可能更合適,從而提供更好的性能和降低複雜性。
Date
、RegExp
、DOM 元素)。例如,當深複製包含函數的對象時,可能會復制函數的引用,但不會復制函數的閉包及其綁定的上下文。同樣,具有特殊特徵的對像在深複製時可能會丟失其獨特的屬性和行為。 JSON.parse(JSON.stringify(obj))
這樣的內置方法也有一些限制,例如無法正確處理函數、循環引用或特殊對象。雖然有一些第三方庫(如 Lodash 的 _.cloneDeep()
)可以更有效地處理深複製,但為深複製添加外部依賴項可能並不總是理想的。 感謝您抽出時間閱讀本文。淺複製與深複製比任何初學者想像的都要復雜得多。儘管每種方法都有很多陷阱,但花時間審查和考慮這些選項將確保您的應用程序和數據保持您想要的樣子。
淺複製和深複製之間的主要區別在於它們處理作為對象的屬性的方式。在淺複製中,複製的對象與原始對象共享對嵌套對象的相同引用。這意味著對嵌套對象的更改將反映在原始對象和復制對像中。另一方面,深複製會創建嵌套對象的新實例,這意味著對複制對像中嵌套對象的更改不會影響原始對象。
JavaScript 中的展開運算符(…)通常用於淺複製。它將一個對象的所有可枚舉自身屬性複製到另一個對象。但是,它只複製第一級的屬性和嵌套對象的引用。因此,對嵌套對象的更改將影響原始對象和復制的對象。
是的,您可以使用 JSON 方法在 JavaScript 中進行深複製。 JSON.stringify()
和 JSON.parse()
方法的組合可以創建對象的深複製。 JSON.stringify()
將對象轉換為字符串,JSON.parse()
將字符串解析回新對象。但是,此方法有一些限制,因為它不會復制方法,並且不適用於 Date
、RegExp
、Map
、Set
等特殊 JavaScript 對象。
淺複製只複製第一級的屬性和嵌套對象的引用。因此,如果原始對象包含嵌套對象,則對這些嵌套對象的更改將影響原始對象和復制的對象。這可能導致代碼中出現意外的結果和錯誤。
Object.assign()
方法如何在淺複製中工作? Object.assign()
方法用於將一個或多個源對象的所有可枚舉自身屬性的值複製到目標對象。它返回目標對象。但是,它執行淺複製,這意味著它只複製第一級的屬性和嵌套對象的引用。
在 JavaScript 中深複製對象的最佳方法取決於代碼的具體要求。如果您的對像不包含方法或特殊的 JavaScript 對象,您可以使用 JSON.stringify()
和 JSON.parse()
方法的組合。對於更複雜的對象,您可能需要使用 Lodash 等庫,該庫提供深克隆函數。
不可以,JavaScript 中的展開運算符只執行淺複製。它複製第一級的屬性和嵌套對象的引用。要執行深複製,您需要使用其他方法或庫。
深複製可能比淺複製更消耗資源,尤其對於大型對象而言。這是因為深複製為所有嵌套對象創建新實例,這可能會佔用更多內存和處理能力。
JSON.stringify()
和 JSON.parse()
等深複製方法不處理循環引用,並將拋出錯誤。循環引用發生在一個對象的屬性引用該對象本身時。要處理循環引用,您需要使用支持它的庫,例如 Lodash。
了解淺複製和深複製之間的區別對於管理 JavaScript 中的數據至關重要。它會影響您的對像如何相互交互。如果您不小心,淺複製會導致意外的結果和錯誤,因為對嵌套對象的更改會影響原始對象和復制的對象。另一方面,深複製確保您的複制對象與原始對象完全獨立。
以上是淺與深層複製的JavaScript的詳細內容。更多資訊請關注PHP中文網其他相關文章!