1. 程式人生 > >js基礎知識(6)-深淺拷貝

js基礎知識(6)-深淺拷貝

let a = {
age: 1
}
let b = a
a.age = 2
console.log(b.age) // 2

從上述例子中我們可以發現,如果給一個變數賦值一個物件,那麼兩者的值會是同一個引用,其中一方改變,另一方也會相應改變。通常在開發中我們不希望出現這樣的問題,我們可以使用淺拷貝來解決這個問題。

淺拷貝


首先可以通過 Object.assign 來解決這個問題。

let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1

當然我們也可以通過展開運算子(…)來解決
 

let a = {
age: 1
}
let b = {...a}
a.age = 2
console.log(b.age) // 1

通常淺拷貝就能解決大部分問題了,但是當我們遇到如下情況就需要使用到深拷貝了
 

let a = {
age: 1,
jobs: {
frst: 'FE'
}
}
let b = {...a}
a.jobs.frst = 'native'
console.log(b.jobs.frst) // native

淺拷貝只解決了第一層的問題,如果接下去的值中還有物件的話,那麼就又回到剛開始的話題了,兩者享有相同的引用。要解決這個問題,我們需要引入深拷貝。


深拷貝
 

這個問題通常可以通過 JSON.parse(JSON.stringify(object)) 來解決。
 

let a = {
age: 1,
jobs: {
frst: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.frst = 'native'
console.log(b.jobs.frst) // FE

但是該方法也是有侷限性的:
• 會忽略 undefined
• 不能序列化函式
• 不能解決迴圈引用的物件
 

let obj = {
a: 1,
b: {
c: 2,
d: 3,
},
}
obj.c = obj.b
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj)

如果你有這麼一個迴圈引用物件,你會發現你不能通過該方法深拷貝

在遇到函式或者 undefined 的時候,該物件也不能正常的序列化
 

let a = {
age: undefned,
jobs: function() {},
name: 'yck'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b) // {name: "yck"}

你會發現在上述情況中,該方法會忽略掉函式和 undefined 。但是在通常情況下,複雜資料都是可以序列化的,所以這個函式可以解決大部分問題,並且該函式是內建函式中處理深拷貝效能最快的。當然如果你的資料中含有以上三種情況下,可以使用 lodash 的深拷貝函式。
如果你所需拷貝的物件含有內建型別並且不包含函式,可以使用 MessageChannel

function structuralClone(obj) {
return new Promise(resolve => {
const {port1, port2} = new MessageChannel();
port2.onmessage = ev => resolve(ev.data);
port1.postMessage(obj);
});
}
var obj = {a: 1, b: {
c: b
}}
// 注意該方法是非同步的
// 可以處理 undefined 和迴圈引用物件
const clone = await structuralClone(obj);