通常在開發中我們不希望出現這樣的問題
let a = {
age: 1
}
let b = a
a.age = 2
console.log(b.age) // 2
對于這種問題,我們就需要用到淺拷貝。
1、使用Object.assign來實現淺拷貝
let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1
2、使用展開運算符 ...來實現淺拷貝
let a = {
age: 1
}
let b = { ...a }
a.age = 2
console.log(b.age) // 1
雖然淺拷貝能解決我們日常開發中遇到的大部分問題,但是仍然還是有一些問題無法解決的。
當我們遇到如下情況就可能需要使用到深拷貝了
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = { ...a }
a.jobs.first = 'native'
console.log(b.jobs.first) // native
淺拷貝只解決了第一層的問題,如果接下去的值中還有對象的話,那么就又回到最開始的話題了,兩者享有相同的地址。要解決這個問題,我們就得使用深拷貝了。
深拷貝
通常可以通過 JSON.parse(JSON.stringify(object)) 來實現深拷貝
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
但是該方法也是有局限性的:
- 會忽略 undefined
- 會忽略 symbol
- 不能序列化函數
- 不能解決循環引用的對象
例如:你有這么一個循環引用對象,你會發現并不能通過該方法實現深拷貝
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)
對于某些情況,我們可以自己實現一個深拷貝,但是這個只是簡易版的,因為其實實現一個深拷貝是很困難的,需要我們考慮好多種邊界情況,比如原型鏈如何處理、DOM 如何處理等等,相對而言,我更加推薦我其實更推薦使用 lodash 的深拷貝函數。
// 深拷貝--簡易版
function deepClone(obj) {
function isObject(o) {
return (typeof o === 'object' || typeof o === 'function') && o !== null
}
if (!isObject(obj)) {
throw new Error('非對象')
}
let isArray = Array.isArray(obj)
let newObj = isArray ? [...obj] : { ...obj }
Reflect.ownKeys(newObj).forEach(key => {
newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
})
return newObj
}
//例子
let obj = {
a: [1, 2, 3],
b: {
c: 2,
d: 3
}
}
let newObj = deepClone(obj)
newObj.b.c = 1
console.log(obj.b.c) // 2