今日介紹:深拷貝、淺拷貝。在實作上應該經常碰到,尤其是當需要處理龐大後端傳來的資料時。
當新舊陣列,會因為資料使用的地址相同,導致任何一方有改變時會影響對方,就是淺拷貝。
通常物件內第一層是是獨立的;但如果是物件,新舊的第二層物件會指向相同的地址、有相同的值,就會互相影響。
Original Object data 與 Cloned Object data有任何一層的資料地址相同,背後指向的值相同,兩物件的操作會相互影響。
以下方法即為各種只複製「值」的淺拷貝方法。
直接複製過去,內容會是一樣的沒有問題。
let data = {
layerOne: 1,
obj: {layerTwo: 10,}
}
let newData
newData = data
console.log(data === newData) // true
// data = {
// layerOne: 1,
// obj: {layerTwo: 10,}
// }
// newData = {
// layerOne: 1,
// obj: {layerTwo: 10,}
// }
//-------------------------------------//情況一
newData.layerOne = 2
newData.obj.layerTwo = 20
console.log(data === newData) // true
// data = {
// layerOne: 2,
// obj: {layerTwo: 20,}
// }
// newData = {
// layerOne: 2,
// obj: {layerTwo: 20,}
// }
//-------------------------------------//情況二
data.layerOne = 3
data.obj.layerTwo = 30
console.log(data === newData) // true
// data = {
// layerOne: 3,
// obj: {layerTwo: 30,}
// }
// newData = {
// layerOne: 3,
// obj: {layerTwo: 30,}
// }
一個個以原陣列來賦值,內容會一樣。
let data = {
layerOne: 1,
obj: {layerTwo: 10,}
}
let newData
// 手動複製
newData = {
layerOne: data.layerOne,
obj: data.obj,
}
console.log(data === newData) // false
// data = {
// layerOne: 1,
// obj: {layerTwo: 10,}
// }
// newData = {
// layerOne: 1,
// obj: {layerTwo: 10,}
// }
//-------------------------------------//情況一
newData.layerOne = 2
newData.obj.layerTwo = 20
// data第一層沒被影響,第二層被影響
// data = {
// layerOne: 1,
// obj: {layerTwo: 20,}
// }
// newData = {
// layerOne: 2,
// obj: {layerTwo: 20,}
// }
//-------------------------------------//情況二
data.layerOne = 3
data.obj.layerTwo = 30
// newData第一層沒被影響,第二層被影響
// data = {
// layerOne: 3,
// obj: {layerTwo: 30,}
// }
// newData = {
// layerOne: 1,
// obj: {layerTwo: 30,}
// }
使用種種 array 的方法
// slice
let a = [1, 2, { layerTwo:3 }];
let b ;
b = a.slice(0);
console.log(a); // [1, 2, { layerTwo:3 }]
console.log(b); // [1, 2, { layerTwo:3 }]
console.log(b === a); // false
// from
let a = [1, 2, { layerTwo:3 }];
let b ;
b = Array.from(a);
console.log(a); // [1, 2, { layerTwo:3 }]
console.log(b); // [1, 2, { layerTwo:3 }]
console.log(b === a); // false
//-------------------------------------//情況一
a[0] = 2
a[2].layerTwo = 4
// b 第一層沒被影響,第二層被影響
// a = [ 2, 2, {layerTwo: 4,}]
// b = [ 1, 2, {layerTwo: 4,}]
//-------------------------------------//情況二
b[0] = 2
b[2].layerTwo = 5
// a 第一層沒被影響,第二層被影響
// a = [ 1, 2, {layerTwo: 5,}]
// b = [ 2, 2, {layerTwo: 5,}]
使用 assign 方法
let data = {
layerOne: 1,
obj: {layerTwo: 10,}
}
let newData
// Object.assign
newData = Object.assign({}, data);
console.log(data === newData) // false
// data = {
// layerOne: 1,
// obj: {layerTwo: 10,}
// }
// newData = {
// layerOne: 1,
// obj: {layerTwo: 10,}
// }
//-------------------------------------//情況一
newData.layerOne = 2
newData.obj.layerTwo = 20
// data第一層沒被影響,第二層被影響
// data = {
// layerOne: 1,
// obj: {layerTwo: 20,}
// }
// newData = {
// layerOne: 2,
// obj: {layerTwo: 20,}
// }
//-------------------------------------//情況二
data.layerOne = 3
data.obj.layerTwo = 30
// newData第一層沒被影響,第二層被影響
// data = {
// layerOne: 3,
// obj: {layerTwo: 30,}
// }
// newData = {
// layerOne: 1,
// obj: {layerTwo: 30,}
// }
使用 ES6的展開符 來複製
let data = {
layerOne: 1,
obj: {layerTwo: 10,}
}
let newData
// Spread operator
newData = {...data};
console.log(data)
console.log(newData)
console.log(data === newData) // false
// data = {
// layerOne: 1,
// obj: {layerTwo: 10,}
// }
// newData = {
// layerOne: 1,
// obj: {layerTwo: 10,}
// }
//-------------------------------------//情況一
newData.layerOne = 2
newData.obj.layerTwo = 20
// data第一層沒被影響,第二層被影響
// data = {
// layerOne: 1,
// obj: {layerTwo: 20,}
// }
// newData = {
// layerOne: 2,
// obj: {layerTwo: 20,}
// }
//-------------------------------------//情況二
data.layerOne = 3
data.obj.layerTwo = 30
// newData第一層沒被影響,第二層被影響
// data = {
// layerOne: 3,
// obj: {layerTwo: 30,}
// }
// newData = {
// layerOne: 1,
// obj: {layerTwo: 30,}
// }
資料不會互相影響。就算是深層的物件,因為它們有各自對應的地址,所以不會受其他物件的改變被影響。
Original Object data 與 Cloned Object data 兩方完全獨立,每一的資料位址都不同,不會互相影響的深層物件
let data = {
layerOne: 1,
obj: {layerTwo: 10,}
}
let newData
// JSON.parse(JSON.stringify())
newData = JSON.parse(JSON.stringify(data));
console.log(data === newData) // false
// data = {
// layerOne: 1,
// obj: {layerTwo: 10,}
// }
// newData = {
// layerOne: 1,
// obj: {layerTwo: 10,}
// }
//-------------------------------------//情況一
newData.layerOne = 2
newData.obj.layerTwo = 20
console.log(data === newData) // false
// data第一層、第二層都沒被影響
// data = {
// layerOne: 1,
// obj: {layerTwo: 10,}
// }
// newData = {
// layerOne: 2,
// obj: {layerTwo: 20,}
// }
//-------------------------------------//情況二
data.layerOne = 3
data.obj.layerTwo = 30
console.log(data === newData) // false
// newData第一層、第二層都沒被影響
// data = {
// layerOne: 3,
// obj: {layerTwo: 30,}
// }
// newData = {
// layerOne: 1,
// obj: {layerTwo: 10,}
// }
使用JSON.parse(JSON.stringify()) 方法會有些要注意的地方,後續補充上來
let data = {
layerOne: 1,
obj: {layerTwo: 10,}
}
let newData = {}
// 自己寫深拷貝函數
function deepCopy(dataNew, dataOld){
for (let key in dataOld) {
if(typeof dataOld[key] === 'object'){
dataNew[key] = {};
deepCopy(dataNew[key], dataOld[key])
}else{
dataNew[key] = dataOld[key];
}
}
}
deepCopy(newData, data)
console.log(data === newData) // false
// data = {
// layerOne: 1,
// obj: {layerTwo: 10,}
// }
// newData = {
// layerOne: 1,
// obj: {layerTwo: 10,}
// }
//-------------------------------------//情況一
newData.layerOne = 2
newData.obj.layerTwo = 20
// data第一層、第二層都沒被影響
// data = {
// layerOne: 1,
// obj: {layerTwo: 10,}
// }
// newData = {
// layerOne: 2,
// obj: {layerTwo: 20,}
// }
//-------------------------------------//情況二
data.layerOne = 3
data.obj.layerTwo = 30
// newData第一層、第二層都沒被影響
// data = {
// layerOne: 3,
// obj: {layerTwo: 30,}
// }
// newData = {
// layerOne: 3,
// obj: {layerTwo: 30,}
// }
Original Object data 與 Cloned Object data有任何一層的資料地址相同,背後指向的值相同,兩物件的操作會相互影響
淺拷貝方式
Original Object data 與 Cloned Object data 兩方完全獨立,每一的資料位址都不同,不會互相影響的深層物件
深拷貝方式
今天用簡單的示例介紹了淺拷貝與深拷貝的幾種方法,我把自己的微小經驗與對其他優秀網站的理解做成筆記,雖然蠻多重複的地方(實驗結果都相同的部分),但因為我當初在理解這塊,是反覆看了幾次才明白深拷貝與淺拷貝的差異的,所以希望以上的超基本實驗對入門新手是友善的(也是想給金魚腦的自己再回來看時一目瞭然)。
今天就到這,如有說明不周或錯誤的地方,還請多留言討論(鞠躬)。
題外話:當初在工作上經常用到,但真正認真去搞清楚其中差異,其實是為了面試(小聲說)。
for...、for...in...
https://blog.csdn.net/gang544043963/article/details/107196754
深淺拷貝。解釋很清楚!
https://www.programfarmer.com/articles/2021/javascript-shallow-copy-deep-copy
拷貝值
https://eudora.cc/posts/210430/
二維陣列轉一維解法
https://blog.camel2243.com/2016/09/02/javascript-二維陣列轉換為一維陣列/