如何在JavaScript中復(fù)制一個對象?
JavaScript的原始數(shù)據(jù)類型是不可變的,這意味著一旦創(chuàng)建,它們的值就不能改變。但是,對象和數(shù)組是可變的,允許在創(chuàng)建后修改它們的值。
實際上,這意味著原始數(shù)據(jù)是通過值傳遞的,而對象和數(shù)組是通過引用傳遞的??紤]以下例子:
let str = 'Hello';
let copy = str;
copy = 'Hi';
// str = 'Hello', copy = 'Hi'
let obj = { a: 1, b: 2 };
let objCopy = obj;
objCopy.b = 4;
// obj = { a: 1, b: 4}, objCopy = { a: 1, b: 4 }
你可以看到,對象obj是通過引用傳遞給objCopy的。修改其中一個變量會影響另一個變量,因為它們都引用同一個對象。那我們?nèi)绾谓鉀Q這個問題呢?答案是克隆對象。
淺復(fù)制
使用展開運算符(...)或 Object.assign(),我們可以克隆對象并根據(jù)其屬性創(chuàng)建一個新對象。
const shallowClone = obj => Object.assign({}, obj);
let obj = { a: 1, b: 2};
let clone = shallowClone(obj);
let otherClone = shallowClone(obj);
clone.b = 4;
otherClone.b = 6;
// obj = { a: 1, b: 2}
// clone = { a: 1, b: 4 }
// otherClone = { a: 1, b: 6 }
這種技術(shù)被稱為淺復(fù)制,因為它適用于外部(淺層)對象,但如果我們有嵌套(深層)對象,則會失敗,因為這些對象最終會通過引用傳遞。這就引出了下一部分。
圖片
深復(fù)制
為了創(chuàng)建一個對象的深復(fù)制,我們需要遞歸地克隆每一個嵌套對象,復(fù)制嵌套對象和數(shù)組。
網(wǎng)上有一些解決方案使用 JSON.stringify() 和 JSON.parse()。雖然這種方法在某些情況下可能有效,但它包含許多問題和性能問題,因此我建議不要使用它。
從邊界情況開始,我們需要檢查傳遞的對象是否為空,如果是,則返回 null。否則,我們可以使用 Object.assign() 和一個空對象 ({}) 來創(chuàng)建原始對象的淺復(fù)制。
然后,我們將使用 Object.keys() 和 Array.prototype.forEach() 來確定需要深復(fù)制哪些鍵值對。如果對象是一個數(shù)組,我們將設(shè)置克隆對象的長度與原對象相等,并使用 Array.from() 創(chuàng)建一個克隆對象。否則,我們將遞歸地調(diào)用函數(shù),把當(dāng)前值作為參數(shù)傳入。
深復(fù)制專用于簡單對象和數(shù)組。這意味著它無法處理類實例,函數(shù)和其他特殊情況。那么,我們?nèi)绾翁幚磉@些情況呢?JavaScript最近給我們提供了一個新的工具來解決這個問題!
使用 structuredClone() 進行深復(fù)制
克隆顯然是一個很常見也很重要的問題。事實上,JavaScript引入了structuredClone()全局函數(shù),可以用來深度克隆對象。而我們不需要實現(xiàn)復(fù)雜的遞歸函數(shù),只需使用這個函數(shù)就可以克隆對象。
這種技術(shù)可用于數(shù)組和對象,代碼最少,是JavaScript中克隆對象的推薦方式,因為它性能最佳且最可靠。