JavaScript 深度複製(Deep Copy)的兩種方式

在 JavaScript 中一般的複製是屬於淺複製(淺拷貝 Shallow Copy),只有複製物件的第一層屬性,而對於物件、陣列則是複製記憶體位置(reference),修改複製的結果會讓原本的資料改變。
    
const people = [
    {name: '老詹', age: 25},
    {name: '小徐', age: 25},
];

console.log(`複製前: ${JSON.stringify(people)}`); // 複製前: 老詹

const copy =  Object.assign([], people);
// const copy = [...people]; // 也會產生一樣的結果

copy[0].name = '小趙';
console.log(`複製後: ${JSON.stringify(people)}`); // 複製後: 小趙
console.log(`複製後: ${JSON.stringify(copy)}`); // 複製後: 小趙

/*
複製前: [{"name":"老詹","age":25},{"name":"小徐","age":25}]
複製後: [{"name":"小趙","age":25},{"name":"小徐","age":25}]
複製後: [{"name":"小趙","age":25},{"name":"小徐","age":25}]
*/
    

如果想要完全不影響則是需要使用深度複製(深拷貝Deep Copy)

方法一: 序列化再返序列化

對於結構較為簡單的可以使用序列化再返序列化,來輕鬆達成深度複製:
    
const people = [
    {name: '老詹', age: 25},
    {name: '小徐', age: 25},
];

console.log(`複製前: ${JSON.stringify(people)}`); // 複製前: 老詹

const copy = JSON.parse(JSON.stringify(people));

copy[0].name = '小趙';
console.log(`複製後: ${JSON.stringify(people)}`); // 複製後: 小趙
console.log(`複製後: ${JSON.stringify(copy)}`); // 複製後: 小趙

/*
複製前: [{"name":"老詹","age":25},{"name":"小徐","age":25}]
複製後: [{"name":"老詹","age":25},{"name":"小徐","age":25}]
複製後: [{"name":"小趙","age":25},{"name":"小徐","age":25}]
*/
    

但是對於日期、函式等反序列化後型態會轉變,例如 Date 會變為字串:
    
let obj = {
    a: new Date()
};

let clone = JSON.parse(JSON.stringify(obj));


console.log(obj.a instanceof Date);  // true
console.log(clone.a instanceof Date); // false
    

方法二: structuredClone 函式

後面 JavaScript 出了一個好東西: structuredClone ,可以輕鬆完成深度複製
從 2022 年 3 月開始在 Chrome, Edge, Firefox, safari 等瀏覽器都已經支援這個 function 了,可以放心使用

上面的範例無法處理的日期可以輕鬆達成深度複製,複製後依然會是日期格式:
    
let obj = {
    a: new Date()
};

let clone = structuredClone(obj)

console.log(obj.a instanceof Date);  // true
console.log(clone.a instanceof Date); // true
    



參考資料:
mdn web docs - structuredClone() global function

留言

張貼留言

如果有任何問題、建議、想說的話或文章題目推薦,都歡迎留言或來信: a@ruyut.com