詳細聊聊JS中不一樣的深拷貝
前言
對于深拷貝這個概念在面試中時常被提起,面試官可能讓你實現(xiàn)深拷貝需要考慮那些因素,或者直接讓你手寫封裝一個深拷貝,那么今天就和大家探討一下一個讓面試官感到牛的深拷貝,
1.思考
眾所周知普通的數(shù)據(jù)類型是值存儲,而復雜類型是通過開辟內存空間來存儲數(shù)據(jù)的,我們通過內存地址從而查找數(shù)據(jù),為了可以完全得到一個與原對象一模一樣但又沒有內存地址關聯(lián)的深拷貝,我們需要考慮的因素其實有很多, 1.Object.create()創(chuàng)造的對象 , Object.create()詳細介紹
let obj = Object.create(null) obj.name = '張三' obj.age = 22
這個對象是一個沒有原型的對象,大部分對象都有自己的原型,可以使用公共的方法,但這個卻不行,我們是不是應該把它考慮進去?
2.symbol作為屬性名的情況 Symbol詳細介紹 以及 for in 詳細介紹
let obj = { name: 'aa', age: 22, [Symbol('a')]: '獨一無二的' }
對于帶有symbol的屬性,在 for in 的迭代中是不可枚舉的,我們是不是需要考慮如何解決?
3.對于修改對象的屬性描述 Object.defineProperty()
let obj = { name: 'ayu', age: 22, sex: '男' } Object.defineProperty(obj, 'age', { enumerable: true, configurable: true, value: 22, writable: false })
這里我們改寫了原對象的屬性描述,age變得無法枚舉,for in 也失去效果,并且很多默認的屬性描述信息,我們是不是在拷貝后也應該和原對象保持一致?
4.對象的循環(huán)引用
let obj = { name: 'ayu', age: 22, sex: '男' } obj.e = e
obj對象中有個e的屬性指向obj,造成相互引用,當我們在封裝深拷貝時,主要是通過遞歸來逐層查找屬性值的情況,然后對其進行操作,如果出現(xiàn)這個情況,就會死循環(huán)遞歸造成棧內存溢出,這種情況難道也不值得考慮嘛?
5.一些特殊的對象 都說萬物皆對象,對象其實有很多類型,正則,日期(Date),等都需要特殊處理 而函數(shù)和數(shù)組就比較簡單
6.深拷貝的多數(shù)要點 也就是當一個對象里面嵌套了多層對象,這個大家應該都知道,我們通常一般使用遞歸去處理,再結合上面分析的因素就可以封裝函數(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) // 日期對象直接返回一個新的日期對象 if (obj.constructor === RegExp) return new RegExp(obj) //正則對象直接返回一個新的正則對象 //如果循環(huán)引用了就用 weakMap 來解決 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這個函數(shù)開始說起
- 1.如果對象的構造器是Date構造器,則我們使用Dte構造器再構造一個Date
- 如果對象的構造器是正則構造器再構造一個正則
- WeakMap我們先不提,allDesc是拿到原對象所有的屬性(可枚舉以及不可枚舉)以及對應的屬性描述信息
- cloneObj是我們根據(jù)第三步拷貝的一個新的對象的信息,不過是一個淺拷貝,而且我們考慮了原型不存在的情況 Object.assin與Object.create的區(qū)別
- 通過for of 循環(huán) Reflect.ownKeys(obj) Reflect.ownKeys()用法 (Reflect.ownKeys()可以遍歷對象自身所有的屬性(symbol,不可枚舉都可以),然后重新將obj的key以及對應的值賦值給cloneObj,并且對obj[key]的值做了討論,當它是對象并且不是函數(shù)時,我們遞歸處理,否則里面為普通值,直接賦給ObjClone
對于deepClone的第二個參數(shù)WeakMap來講, 請大家想想最開始我們提到的一個問題,我們有一個對象,然后我們填了了一個屬性,屬性為這個對象,這是在相互引用,如果我們處理這樣的對象,也使用遞歸處理,那么就是死循環(huán),因此我們需要一個數(shù)據(jù)結構來解決,每次我們遞歸處理的時候,都把obj,以及賦值的cloneobj對應存儲,當遇到死循環(huán)的時候直接return這個對象即可 WeakMap詳細介紹·
(本文用到大量ES5以后的API,推薦閱讀阮一峰老師的ES6,這樣才能理解的透徹)
總結
到此這篇關于JS中不一樣的深拷貝的文章就介紹到這了,更多相關JS深拷貝內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
JavaScript之RegExp_動力節(jié)點Java學院整理
正則表達式是一種用來匹配字符串的強有力的武器。它的設計思想是用一種描述性的語言來給字符串定義一個規(guī)則,凡是符合規(guī)則的字符串,我們就認為它“匹配”了,否則,該字符串就是不合法的2017-06-06