JavaScript 拷貝對象的幾種方法
對象與原始類型的根本區別之一是,對象是 “通過引用” 存儲和複製的,而原始類型:字符串、數字、布爾值等 —— 總是 “作爲一個整體” 複製。賦值了對象的變量存儲的不是對象本身,而是該對象“在內存中的地址” —— 換句話說就是對該對象的“引用”。
接下來我們來看看如何拷貝對象。
淺拷貝
在 JavaScript 中複製一個對象幾乎都是淺層的(只拷貝一層),而不是深層的。這意味着對一個對象深度嵌套的值的做出修改,同時也將修改它的副本上的值。因爲它們引用的是同一個對象。
淺拷貝常用的方法
for...in
循環
for...in
語句以任意順序迭代一個對象的除 Symbol 以外的可枚舉屬性,包括繼承的可枚舉屬性。可以通過 for in 實現淺拷貝。
function shallowCopy(obj) {
const newObj = {};
for (const key in obj) {
newObj[key] = obj[key];
}
return newObj;
}
Object.assign()
Object.assign() 方法用於將所有可枚舉屬性的值從一個或多個源對象複製到目標對象。它將返回目標對象。
const obj = { a: 1, b: 2, c: 3 };
const copy = Object.assign({}, obj);
console.log(copy); // { a: 1, b: 2, c: 3 }
嵌套對象的拷貝
const obj = { a: 1, b: 2, c: { d: 4 } };
const copy = Object.assign({}, obj);
copy.c.d = 5;
console.log(obj); // { a: 1, b: 2, c: { d: 5 } }
原來的對象也被修改了。
spread 擴展運算符
const obj = { a: 1, b: 2, c: 3 };
const copy = { ...obj };
console.log(copy); // { a: 1, b: 2, c: 3 }
深拷貝
深拷貝是指將一個對象從內存中完整的拷貝一份出來,從堆內存中開闢一個新的區域存放新對象,且修改新對象不會影響原對象。
deepCopy
前面我們通過 for in 寫了一個 shallowCopy
,我們將它改名爲 deepCopy
。通過屬性是否爲引用類型,遞歸調用 deepCopy
,來實現深拷貝。
const deepCopy = (obj) => {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
const clone = Array.isArray(obj) ? [] : {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepCopy(obj[key]);
}
}
return clone;
}
對象轉換爲 JSON 字符串,再轉換爲對象
const obj = { a: 1, b: 2, c: { d: 4 } };
const copy = JSON.parse(JSON.stringify(obj));
copy.c.d = 5;
console.log(obj); // { a: 1, b: 2, c: { d: 4 } }
JSON.parse(JSON.stringify(obj))
可比上面 deepClone 函數簡潔多了,但不是好的實踐,因爲它有以下缺點:
-
• 會忽略 undefined
-
• 會忽略 symbol
-
• 不能序列化函數
-
• 不能解決循環引用的對象
-
• 如果 data 裏有 NaN、Infinity 和 - Infinity,則序列化的結果會變成 null
-
• 致命缺陷,性能差
JSON.parse(JSON.stringify(obj)) 的缺點
致命缺陷,性能差
structuredClone
structuredClone
structuredClone
是 HTML5 一個新的 API,它可以實現深拷貝。
const obj = { a: 1, b: 2, c: { d: 4 } };
const copy = structuredClone(obj);
copy.c.d = 5;
console.log(obj); // { a: 1, b: 2, c: { d: 4 } }
該方法還支持把原始值中的 transferable objects (en-US) (可轉移對象) 轉移到新對象,而不是把屬性引用拷貝過去。可轉移對象與原始對象分離並附加到新對象; 它們不可以在原始對象中訪問被訪問到。
目前主流瀏覽器和 js 運行時都支持該方法,可放心食用。
structuredClone
參考資料
-
• 對象引用和複製
-
• Why JSON.parse(JSON.stringify()) is a bad practice to clone an object in JavaScript | by Petro Zubar | Medium
-
• structuredClone() - Web 開發技術 | MDN
-
• Deep-copying in JavaScript using structuredClone
歡迎關注我的公衆號 “碼中人”,原創技術文章第一時間推送。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/obfJgS0kUO3ZnIDnREOWBg