關(guān)于對(duì)象的深淺克隆
首先在我們的認(rèn)知中嫉父,存在這復(fù)合數(shù)據(jù)類(lèi)型壶冒,和基本數(shù)據(jù)類(lèi)型〗矗基本數(shù)據(jù)類(lèi)型在使用的時(shí)候是進(jìn)行傳值調(diào)用址,復(fù)合數(shù)據(jù)類(lèi)型是進(jìn)行一個(gè)傳址調(diào)用而咆。
所以我們?cè)趯?duì)象克隆的時(shí)候就會(huì)錯(cuò)誤的將同一個(gè)地址賦值到我們需要使用的一個(gè)新的變量之上,這樣我們?cè)谑褂玫臅r(shí)候幕袱。修改值得時(shí)候就會(huì)直接在地址中修改暴备,所以就會(huì)影響到原數(shù)組,這時(shí)候我們就可能會(huì)需要用到對(duì)象得深克隆
-
首先我先介紹幾種淺拷貝得方法來(lái)避免使用们豌。
- Object.assign(),這是ES6中合并對(duì)象得方法涯捻,這是把第一個(gè)參數(shù)之后得參數(shù)全部復(fù)制到第一個(gè)對(duì)象中,這樣可以實(shí)現(xiàn)第一層的拷貝
- 然后我們也可以利用ES6的展開(kāi)運(yùn)算符來(lái)實(shí)現(xiàn)對(duì)第一層的淺拷貝。
-
接著我們就介紹一下深拷貝了
- 最常用的一種拷貝方式
JSON.parse(JSON.stringify(object))
該方法引發(fā)的問(wèn)題:
- 會(huì)忽略u(píng)ndefined
- 會(huì)忽略到symbol
- 不能序列化函數(shù)
- 不能解決循環(huán)引用的對(duì)象
- 我們還可以使用lodash中的
cloneDeep
函數(shù)來(lái)進(jìn)行對(duì)象的深克隆 - 最后我們可以使用自己的遞歸方法來(lái)進(jìn)行深克隆望迎。
1.首先我們先判斷傳入的對(duì)象類(lèi)型障癌,然后我們根據(jù)這個(gè)類(lèi)型來(lái)創(chuàng)建target
2.我們?cè)讷@取源對(duì)象所有的屬性名,包括不可枚舉的屬性
3.之后我們遍歷所有的屬性名
4.我們獲取每個(gè)屬性的描述對(duì)象
5.在判斷該屬性是否是基礎(chǔ)數(shù)據(jù)類(lèi)型擂煞,如果是混弥,我們就直接將當(dāng)前屬性放入目標(biāo)對(duì)象中
6.如果不是的話(huà)趴乡,我們就根據(jù)該屬性对省,重新創(chuàng)建一個(gè)引用
7.因?yàn)镈OM非常的特殊蝗拿,所以我們先判斷該對(duì)象是否是DOM類(lèi)型,如果是的話(huà)我們直接復(fù)制當(dāng)前dom
8.如果不是DOM,我們來(lái)判斷當(dāng)前屬性中原型的構(gòu)造
9.我們根據(jù)構(gòu)造來(lái)創(chuàng)建一個(gè)類(lèi)型
10.之后我們判斷該屬性的某個(gè)描述對(duì)象是否存在蒿涎,如果存在哀托,我們就給他們添加,
11.這些完畢劳秋,我們?cè)诎旬?dāng)前引用傳入當(dāng)前函數(shù)仓手,進(jìn)行遞歸操作。
最后我們將復(fù)制的后的值玻淑,返回出去function cloneObj(source, target) { var list = ["string", "number", "null", "undefined", "boolean", "function"] if (target === undefined) { // 先判斷是不是dom節(jié)點(diǎn),是就可以去直接clone節(jié)點(diǎn) if (HTMLElement.prototype.isPrototypeOf(source)) { target = source.cloneNode(false) } else { target = new source.constructor() } } // 獲取對(duì)象的所有屬性包括嗽冒,包擴(kuò)不可枚舉的屬性 var names = Object.getOwnPropertyNames(source) for (var i = 0; i < names.length; i++) { var desc = Object.getOwnPropertyDescriptor(source, names[i]) if (list.includes(typeof desc.value)) { Object.defineProperty(target, names[i], desc); } else { var t; if(HTMLElement.prototype.isPrototypeOf(desc.value)) { t = desc.value.cloneNode(false) } else { switch (desc.value.constructor) { case RegExp: t = new RegExp(desc.value.source, desc.value.flags) break; case Date: t = new Date(desc.value) break; case Symbol: t = Symbol break; case Set: t = new Set(desc.value.values()) break; case Map: t = new Map(desc.value.entries()) break default: t = new desc.value.constructor(); break; } } var o = {} o.value = t; desc.enumerable && (o.enumerable = desc.enumerable) desc.writable && (o.writable = desc.writable) desc.configurable && (o.configurable = desc.configurable) desc.set && (o.set = desc.set) desc.get && (o.get = desc.get) Object.defineProperty(target, names[i], o) console.log(desc.value,t) cloneObj(desc.value, t) } } return target }
- 最常用的一種拷貝方式