詳細(xì)聊聊JS中不一樣的深拷貝
前言
對(duì)于深拷貝這個(gè)概念在面試中時(shí)常被提起,面試官可能讓你實(shí)現(xiàn)深拷貝需要考慮那些因素,或者直接讓你手寫(xiě)封裝一個(gè)深拷貝,那么今天就和大家探討一下一個(gè)讓面試官感到牛的深拷貝,
1.思考
眾所周知普通的數(shù)據(jù)類(lèi)型是值存儲(chǔ),而復(fù)雜類(lèi)型是通過(guò)開(kāi)辟內(nèi)存空間來(lái)存儲(chǔ)數(shù)據(jù)的,我們通過(guò)內(nèi)存地址從而查找數(shù)據(jù),為了可以完全得到一個(gè)與原對(duì)象一模一樣但又沒(méi)有內(nèi)存地址關(guān)聯(lián)的深拷貝,我們需要考慮的因素其實(shí)有很多, 1.Object.create()創(chuàng)造的對(duì)象 , Object.create()詳細(xì)介紹
let obj = Object.create(null) obj.name = '張三' obj.age = 22
這個(gè)對(duì)象是一個(gè)沒(méi)有原型的對(duì)象,大部分對(duì)象都有自己的原型,可以使用公共的方法,但這個(gè)卻不行,我們是不是應(yīng)該把它考慮進(jìn)去?
2.symbol作為屬性名的情況 Symbol詳細(xì)介紹 以及 for in 詳細(xì)介紹
let obj = { name: 'aa', age: 22, [Symbol('a')]: '獨(dú)一無(wú)二的' }
對(duì)于帶有symbol的屬性,在 for in 的迭代中是不可枚舉的,我們是不是需要考慮如何解決?
3.對(duì)于修改對(duì)象的屬性描述 Object.defineProperty()
let obj = { name: 'ayu', age: 22, sex: '男' } Object.defineProperty(obj, 'age', { enumerable: true, configurable: true, value: 22, writable: false })
這里我們改寫(xiě)了原對(duì)象的屬性描述,age變得無(wú)法枚舉,for in 也失去效果,并且很多默認(rèn)的屬性描述信息,我們是不是在拷貝后也應(yīng)該和原對(duì)象保持一致?
4.對(duì)象的循環(huán)引用
let obj = { name: 'ayu', age: 22, sex: '男' } obj.e = e
obj對(duì)象中有個(gè)e的屬性指向obj,造成相互引用,當(dāng)我們?cè)诜庋b深拷貝時(shí),主要是通過(guò)遞歸來(lái)逐層查找屬性值的情況,然后對(duì)其進(jìn)行操作,如果出現(xiàn)這個(gè)情況,就會(huì)死循環(huán)遞歸造成棧內(nèi)存溢出,這種情況難道也不值得考慮嘛?
5.一些特殊的對(duì)象 都說(shuō)萬(wàn)物皆對(duì)象,對(duì)象其實(shí)有很多類(lèi)型,正則,日期(Date),等都需要特殊處理 而函數(shù)和數(shù)組就比較簡(jiǎn)單
6.深拷貝的多數(shù)要點(diǎn) 也就是當(dāng)一個(gè)對(duì)象里面嵌套了多層對(duì)象,這個(gè)大家應(yīng)該都知道,我們通常一般使用遞歸去處理,再結(jié)合上面分析的因素就可以封裝函數(shù)了
const isComplexDataType = (obj) => (typeof obj === 'object' || typeof obj === 'function') && obj !== null const deepClone = function (obj, hash = new WeakMap()) { if (obj.constructor === Date) return new Date(obj) // 日期對(duì)象直接返回一個(gè)新的日期對(duì)象 if (obj.constructor === RegExp) return new RegExp(obj) //正則對(duì)象直接返回一個(gè)新的正則對(duì)象 //如果循環(huán)引用了就用 weakMap 來(lái)解決 if (hash.has(obj)) return hash.get(obj) let allDesc = Object.getOwnPropertyDescriptors(obj) //遍歷傳入?yún)?shù)所有鍵的特性 let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc) //繼承原型鏈 hash.set(obj, cloneObj) for (let key of Reflect.ownKeys(obj)) { cloneObj[key] = isComplexDataType(obj[key]) && typeof obj[key] !== 'function' ? deepClone(obj[key], hash) : obj[key] } return cloneObj }
思路 從deepclone這個(gè)函數(shù)開(kāi)始說(shuō)起
- 1.如果對(duì)象的構(gòu)造器是Date構(gòu)造器,則我們使用Dte構(gòu)造器再構(gòu)造一個(gè)Date
- 如果對(duì)象的構(gòu)造器是正則構(gòu)造器再構(gòu)造一個(gè)正則
- WeakMap我們先不提,allDesc是拿到原對(duì)象所有的屬性(可枚舉以及不可枚舉)以及對(duì)應(yīng)的屬性描述信息
- cloneObj是我們根據(jù)第三步拷貝的一個(gè)新的對(duì)象的信息,不過(guò)是一個(gè)淺拷貝,而且我們考慮了原型不存在的情況 Object.assin與Object.create的區(qū)別
- 通過(guò)for of 循環(huán) Reflect.ownKeys(obj) Reflect.ownKeys()用法 (Reflect.ownKeys()可以遍歷對(duì)象自身所有的屬性(symbol,不可枚舉都可以),然后重新將obj的key以及對(duì)應(yīng)的值賦值給cloneObj,并且對(duì)obj[key]的值做了討論,當(dāng)它是對(duì)象并且不是函數(shù)時(shí),我們遞歸處理,否則里面為普通值,直接賦給ObjClone
對(duì)于deepClone的第二個(gè)參數(shù)WeakMap來(lái)講, 請(qǐng)大家想想最開(kāi)始我們提到的一個(gè)問(wèn)題,我們有一個(gè)對(duì)象,然后我們填了了一個(gè)屬性,屬性為這個(gè)對(duì)象,這是在相互引用,如果我們處理這樣的對(duì)象,也使用遞歸處理,那么就是死循環(huán),因此我們需要一個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)解決,每次我們遞歸處理的時(shí)候,都把obj,以及賦值的cloneobj對(duì)應(yīng)存儲(chǔ),當(dāng)遇到死循環(huán)的時(shí)候直接return這個(gè)對(duì)象即可 WeakMap詳細(xì)介紹·
(本文用到大量ES5以后的API,推薦閱讀阮一峰老師的ES6,這樣才能理解的透徹)
總結(jié)
到此這篇關(guān)于JS中不一樣的深拷貝的文章就介紹到這了,更多相關(guān)JS深拷貝內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS實(shí)現(xiàn)簡(jiǎn)易換圖時(shí)鐘功能分析
這篇文章主要介紹了JS實(shí)現(xiàn)簡(jiǎn)易換圖時(shí)鐘功能,結(jié)合實(shí)例形式分析了javascript結(jié)合時(shí)間函數(shù)定時(shí)變換顯示圖片實(shí)現(xiàn)時(shí)鐘功能的相關(guān)操作技巧,代碼中備有較為詳盡的注釋便于理解,需要的朋友可以參考下2018-01-01javascript下查找父節(jié)點(diǎn)的簡(jiǎn)單方法
javascript下查找父節(jié)點(diǎn)的簡(jiǎn)單方法...2007-08-08利用Three.js如何實(shí)現(xiàn)陰影效果實(shí)例代碼
使用three.js可以方便的讓我們?cè)诰W(wǎng)頁(yè)中做出各種不同的3D效果,下面這篇文章主要給大家介紹了關(guān)于利用Three.js如何實(shí)現(xiàn)陰影效果的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-09-09JavaScript之RegExp_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
正則表達(dá)式是一種用來(lái)匹配字符串的強(qiáng)有力的武器。它的設(shè)計(jì)思想是用一種描述性的語(yǔ)言來(lái)給字符串定義一個(gè)規(guī)則,凡是符合規(guī)則的字符串,我們就認(rèn)為它“匹配”了,否則,該字符串就是不合法的2017-06-06微信小程序?qū)崿F(xiàn)錨點(diǎn)跳轉(zhuǎn)
這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)錨點(diǎn)跳轉(zhuǎn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11JavaScript無(wú)縫滾動(dòng)效果的實(shí)例代碼
本文給大家分享一段實(shí)例代碼有關(guān)js實(shí)現(xiàn)無(wú)縫滾動(dòng)效果,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2017-03-03詳解處理bootstrap4不支持遠(yuǎn)程靜態(tài)框問(wèn)題
這篇文章主要介紹了詳解處理bootstrap4不支持遠(yuǎn)程靜態(tài)框問(wèn)題,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07