深淺拷貝
對象類型在賦值的過程中實(shí)際上是復(fù)制了地址屎飘,從而導(dǎo)致了其中一方被改變其他也都被改變的情況妥曲,在開發(fā)中我們通常不希望出現(xiàn)這樣的問題,這里可以使用淺拷貝來解決這個情況钦购。
let a = { name: "Jack" }
let b = a
a.name = "Rose"
console.log(b.name) // Rose
什么是淺拷貝檐盟?如何實(shí)現(xiàn)淺拷貝?
首先我們可以通過Object.assign來實(shí)現(xiàn)淺拷貝肮雨,該函數(shù)只會拷貝所有的屬性值到新的對象中遵堵,如果屬性值是對象的話箱玷,拷貝的是地址怨规,即為淺拷貝而不是深拷貝。
以下為Object.assign淺拷貝的簡單實(shí)現(xiàn):
let a = { name: "Jack" }
let b = Object.assign({}, a)
a.name = "Rose"
console.log(b.name) // Jack
還可以通過展開運(yùn)算符...來實(shí)現(xiàn)淺拷貝:
let a = { name: "Jack" }
let b = { ...a }
a.name = "Rose"
console.log(b.name) // Jack
但是锡足,淺拷貝只解決了第一層的問題波丰,如果對象下還有對象的話,那么又回到最開始的問題了舶得,第二層的對象拷貝過來的只是地址掰烟,兩者享有相同的地址,這時(shí)就需要用到深拷貝了沐批。
什么是深拷貝纫骑?如何實(shí)現(xiàn)深拷貝?
我們通常使用JSON.parse(JSON.stringify(object))來解決:
let a = {
name: "Jack",
parents: { mother: "Mom" }
}
let b = JSON.parse(JSON.stringify(a))
a.parents.mother = "Mama"
console.log(b.parents.mother) // Mom
但是該方法具有以下局限性:
- 會忽略undefined
- 會忽略symbol
- 不能序列化函數(shù)
- 不能解決循環(huán)引用的對象
遇到函數(shù)九孩、undefined和symbol時(shí)先馆,會直接忽略掉他們,該對象不能正常的序列化躺彬,此時(shí)我們需要實(shí)現(xiàn)一個更為完善的深拷貝煤墙。
手寫一個深拷貝
function deepClone(obj) {
// 首先刨除數(shù)組和對象外的所有類型,包括null
if (typeof obj !== 'object' || obj == null) {
return obj
}
// 創(chuàng)建一個result變量宪拥,存入深拷貝的結(jié)果
let result
// 判斷obj是數(shù)組還是對象仿野,并相應(yīng)初始化
if (obj instanceof Array) {
result = []
} else {
result = {}
}
// 采用遞歸的思想,將每一層嵌套deepClone修改
for (let key in obj) {
// 在拷貝時(shí)我們只拷貝對象的原有屬性她君,而不拷貝其原型的屬性
if (obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key])
}
}
// 最后返回深拷貝的結(jié)果
return result
}
小結(jié):以上深拷貝的方法依然只是較為簡易的脚作,要想實(shí)現(xiàn)一個比較完美的深拷貝其實(shí)是很困難的,需要我們考慮很多種邊界情況缔刹,比如原型鏈如何處理球涛、DOM如何處理等魄梯。該deepClone函數(shù)就有兩個較為明顯的問題,一是沒有解決對象的循環(huán)引用的問題(參考方案:用弱映射做一個哈希表宾符,存儲原對象酿秸,若緩存命中,則過濾本次拷貝魏烫,直接使用記憶化數(shù)據(jù)辣苏,否則惰性拷貝。一般不是為了解決IE的兼容性問題哄褒,都沒有問題稀蟋,考慮兼容性則按需墊片。生產(chǎn)環(huán)境其實(shí)還需要考慮其它類型的拷貝呐赡,一般直接使用輔助工具庫退客。總而言之链嘀,按需拷貝)萌狂;二是無法實(shí)現(xiàn)函數(shù)的拷貝。