網(wǎng)站建設(shè)需要政府集中采購嗎免費(fèi)廣告投放網(wǎng)站
前言
在js中我們想要實(shí)現(xiàn)深拷貝,首先要了解深淺拷貝的區(qū)別。
淺拷貝:只是拷貝數(shù)據(jù)的內(nèi)存地址,而不是在內(nèi)存中重新創(chuàng)建一個(gè)一模一樣的對象(數(shù)組)
深拷貝:在內(nèi)存中開辟一個(gè)新的存儲(chǔ)空間,完完全全的拷貝一整個(gè)一模一樣的對象(數(shù)組)
1. MessageChannel實(shí)現(xiàn)深拷貝
MDN資料:https://developer.mozilla.org/zh-CN/docs/Web/API/MessageChannel
MDN中關(guān)于MessageChannel
的介紹
Channel Messaging
API
的 MessageChannel
接口允許我們創(chuàng)建一個(gè)新的消息通道,并通過它的兩個(gè) MessagePort
屬性發(fā)送數(shù)據(jù)。
備注: 此特性在 Web Worker 中可用
使用方法:
封裝一個(gè)deepclone函數(shù)
function deppClone(obj) {return new Promise(resolve => {const { port1, port2 } = new MessageChannel()port1.postMessage(obj)port2.onmessage = e => {resolve(e.data)}})
}
測試代碼:
function deppClone(obj) {return new Promise(resolve => {const { port1, port2 } = new MessageChannel()port1.postMessage(obj)port2.onmessage = e => {resolve(e.data)}})
}
const obj = { a: "", c: undefined, e: 0, f: [], g: NaN, h: null }
obj.b = objlet newObj = null
await deppClone(obj).then(res => {newObj = res
})console.log("obj", obj)
console.log("newObj", newObj)
console.log(obj === newObj)
console.log(obj.b === newObj)
console.log(obj === newObj.b)
結(jié)果:
優(yōu)點(diǎn):
他可以完美解決循環(huán)引用的問題。
支持的瀏覽器版本比較多 (文獻(xiàn)資料)
缺點(diǎn):
不支持拷貝函數(shù)
不支持拷貝Symbol
2. structuredClone實(shí)現(xiàn)深拷貝
文獻(xiàn)資料:https://developer.mozilla.org/zh-CN/docs/web/api/structuredClone
MDN中關(guān)于structuredClone()
的介紹
全局的 structuredClone()
方法使用結(jié)構(gòu)化克隆算法將給定的值進(jìn)行深拷貝。
該方法還支持把原始值中的可轉(zhuǎn)移對象轉(zhuǎn)移到新對象,而不是把屬性引用拷貝過去。 可轉(zhuǎn)移對象與原始對象分離并附加到新對象;它們不可以在原始對象中訪問被訪問到。
使用方法:
structuredClone(obj)
測試代碼:
const obj = { a: "",c:undefined,e:0,f:[],g:NaN,h:null }
obj.b = objconst newObj = structuredClone(obj)console.log('obj',obj)
console.log('newObj',newObj)
console.log(obj === newObj)
console.log(obj.b === newObj)
console.log(obj === newObj.b)
結(jié)果:
優(yōu)點(diǎn):
他可以完美解決循環(huán)引用的問題。
而且非常簡單,調(diào)用API即可。
缺點(diǎn):
這是一個(gè)新的API,它支持的瀏覽器版本比較新
不支持拷貝函數(shù)
不支持拷貝Symbol
3.手動(dòng)封裝deepClone函數(shù)
最經(jīng)典的就是手動(dòng)封裝一個(gè)deepClone函數(shù),去主動(dòng)判斷傳入類型并且遞歸創(chuàng)建新的對象
使用方法:
封裝函數(shù),并判斷類型
function isObject(value) {const valueType = typeof valuereturn (value !== null) && (valueType === "object" || valueType === "function")
}function deepClone(originValue, map = new WeakMap()) {// 判斷是否是一個(gè)Set類型if (originValue instanceof Set) {return new Set([...originValue])}// 判斷是否是一個(gè)Map類型if (originValue instanceof Map) {return new Map([...originValue])}// 判斷如果是Symbol的value, 那么創(chuàng)建一個(gè)新的Symbolif (typeof originValue === "symbol") {return Symbol(originValue.description)}// 判斷如果是函數(shù)類型, 那么直接使用同一個(gè)函數(shù)if (typeof originValue === "function") {return originValue}// 判斷傳入的originValue是否是一個(gè)對象類型if (!isObject(originValue)) {return originValue}if (map.has(originValue)) {return map.get(originValue)}// 判斷傳入的對象是數(shù)組, 還是對象const newObject = Array.isArray(originValue) ? []: {}map.set(originValue, newObject)for (const key in originValue) {newObject[key] = deepClone(originValue[key], map)}// 對Symbol的key進(jìn)行特殊的處理const symbolKeys = Object.getOwnPropertySymbols(originValue)for (const sKey of symbolKeys) {// const newSKey = Symbol(sKey.description)newObject[sKey] = deepClone(originValue[sKey], map)}return newObject
}
測試代碼:
let s1 = Symbol("aaa")
let s2 = Symbol("bbb")const obj = {a: 18,b: {c: "www",d: {e: "www"}},// 數(shù)組類型hobbies: ["abc", "cba", "nba"],// 函數(shù)類型foo: function(m, n) {console.log("wwww")console.log("wwww")return 123},// Symbol作為key和value[s1]: "abc",s2: s2,// Set/Mapset: new Set(["aaa", "bbb", "ccc"]),map: new Map([["aaa", "abc"], ["bbb", "cba"]])
}obj.info = objconst newObj = deepClone(obj)
console.log(newObj === obj)
console.log('obj',obj)
console.log('newObj',newObj)
console.log(newObj.s2 === obj.s2)
結(jié)果:
優(yōu)點(diǎn):
他可以完美解決各種問題。
4. JSON實(shí)現(xiàn)深拷貝
這是最不推薦使用的方法
優(yōu)點(diǎn):這是最簡單的方式了,只能處理不復(fù)雜的對象
const symbol1 = Symbol();
const obj = { a: "",c:undefined,e:0,f:[],g:NaN,h:null,i:symbol1 }let newObj = JSON.parse(JSON.stringify(obj))console.log('obj',obj)
console.log('newObj',newObj)
缺點(diǎn):
不能解決循環(huán)引用
NaN問題
忽略Symbol(),undefined
5.其他
比如lodash、jQuery等插件的實(shí)現(xiàn)方式就不多講了。