前言
JavaScript的淺拷貝旨别、深拷貝是一個(gè)老生常談的話題递览,真正完美的深拷貝其實(shí)是比較困難的荚坞,但相對(duì)的能應(yīng)用的場(chǎng)景也同樣比較少懒鉴,個(gè)人感覺(jué)淺拷貝和深拷貝的核心概念無(wú)非是對(duì)JavaScript引用類型的理解,普通的值類型可以直接復(fù)制钮蛛,應(yīng)用類型的直接復(fù)制則是對(duì)其地址的復(fù)制甸饱,這個(gè)其實(shí)就是C語(yǔ)言中的指針的概念驼壶。明白了這些深淺拷貝就不難理解了般妙,接下來(lái)分享一些常用的操作。
淺拷貝
淺拷貝就是不考慮引用類型,我們把對(duì)象的屬性或是數(shù)組的元素都當(dāng)作原始類型來(lái)看待。
Object.assign()
這個(gè)方法是用來(lái)合并兩個(gè)對(duì)象的屬性,將要拷貝的對(duì)象和一個(gè)空對(duì)象合并就能成為一個(gè)新的對(duì)象
cosnt temp = { a: 1, b: 2}
cosnt obj = Object.assign({}, temp)
temp === obj // false
es6擴(kuò)展運(yùn)算符 ...
cosnt temp = { a: 1, b: 2}
cosnt obj = { ...temp }
這種方法可以將temp里的屬性提取出來(lái)放到新的對(duì)象里面
const array = [1,2,3,4,5]
cosnt newArray = [...array]
數(shù)組也是同理
深拷貝
深拷貝要比淺拷貝難實(shí)現(xiàn)的多罕容,淺拷貝的方式會(huì)把引用類型的地址直接復(fù)制過(guò)去旅择,不同的兩個(gè)對(duì)象里面的引用類型的屬性會(huì)互相影響汇歹,這種有時(shí)候不會(huì)符合我們的要求,此時(shí)就需要深拷貝早抠。
JSON.parse(JSON.stringify())
這種配合使用算是比較常用的一種深拷貝方式了甘苍,將對(duì)象轉(zhuǎn)成字符串,再重新生成對(duì)象,這樣新生成的對(duì)象的引用類型和原對(duì)象里面的就不同了簸淀。
cosnt temp = { a: 1, b: 2, c: { d: 1 } }
cosnt obj = JSON.parse(JSON.stringif(temp))
temp === obj // false
temp.c === obj.c // false
這種方式的缺陷是undefined, 函數(shù)和對(duì)象內(nèi)部循環(huán)引用的屬性無(wú)法拷貝
MessageChannel
MessageChannel的介紹可以查看MDN,這個(gè)API其實(shí)是建立一個(gè)消息管道來(lái)進(jìn)行頁(yè)面通信使用的,使用這個(gè)API的比JSON的方法的優(yōu)勢(shì)在于可以進(jìn)行有循環(huán)引用對(duì)象的拷貝,但是依然無(wú)法拷貝函數(shù)
function deepClone(obj) {
return new Promise(resolve => {
const { port1, port2 } = new MessageChannel()
port2.onmessage = event => {
return resolve(event.data)
}
port1.postMessage(obj)
})
}
const temp = { a: 1, b: 2, c: { d: 1 } }
deepClone(temp).then(obj => {
console.log(obj)
})
_.cloneDeep(value)
_.cloneDeep()
是lodash庫(kù)所提供的深拷貝函數(shù)扇住,也是個(gè)人比較推薦的一種票灰,基本可以滿足深拷貝的要求,不需要我們?cè)僮约簩懝ぞ哳悺?/p>
自定義工具類deeepClone
最后當(dāng)然得自己實(shí)現(xiàn)一個(gè)所袁,但是一個(gè)完美的深拷貝函數(shù)要考慮的東西太多了畅涂,比如要不要考慮原型鏈上的屬性、Dom對(duì)象如何處理立宜、函數(shù)如何處理等,所以我提供了一個(gè)不太完善的深拷貝函數(shù),這個(gè)函數(shù)對(duì)于對(duì)象方法屬性的深拷貝也是想了比較久才寫出來(lái)的灯帮。
function deepClone(obj, parent) {
function isObj(obj) {
return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function isFunction(obj) {
return typeof obj === 'function'
}
if(!isObj(obj)) {
return obj
}
if(isFunction(obj)) {
return obj.bind(parent)
}
const newObj = Array.isArray(obj) ? [...obj] : {...obj}
Reflect.ownKeys(obj).forEach(key => {
newObj[key] = isObj(newObj[key]) ? deepClone(newObj[key], newObj) : newObj[key]
})
return newObj
}
let temp = { a: 123, b: { c: function () {return 3} } }
let obj = deepClone(temp)
結(jié)語(yǔ)
每寫一篇文章感覺(jué)都是對(duì)知識(shí)的總結(jié)提煉,雖然沒(méi)人看也要堅(jiān)持下去。
如果有疏漏的地方锣吼,歡迎指出