javascript中的深復(fù)制詳解及實(shí)例分析
javascript中的深復(fù)制
JavaScript深拷貝是初學(xué)者甚至有經(jīng)驗(yàn)的開發(fā)著,都會經(jīng)常遇到問題,并不能很好的理解javascript的深拷貝。
深拷貝(deepClone)是神馬,與深拷貝相對應(yīng)的就是淺拷貝,剛開始我也沒弄懂。
在很多情況下,我們都需要給變量賦值,給內(nèi)存地址賦予一個(gè)值,但是在賦值引用值類型的時(shí)候,只是共享一個(gè)內(nèi)存區(qū)域,導(dǎo)致賦值的時(shí)候,還跟之前的值保持一直性。
看一個(gè)具體的例子
// 給test賦值了一個(gè)對象 var test = { a: 'a', b: 'b' }; // 將test賦值給test2 // 此時(shí)test和test2是共享了同一塊內(nèi)存對象,這也就是淺拷貝 var test2 = test; test2.a = 'a2'; test.a === 'a2'// 為true
如下圖:
這下就很好理解為什么引用值類型數(shù)據(jù)相互影響問題。
實(shí)現(xiàn)一個(gè)深拷貝函數(shù),就不得不說javascript的數(shù)值類型。下面我們先來看一個(gè)js有哪幾種數(shù)據(jù)類型:
類型 | 描述 |
---|---|
undefined | undefined類型只有一個(gè)值undefined,它是變量未被賦值時(shí)的值 |
null | null類型也只有一個(gè)值,它是一個(gè)空對象的引用 |
Boolean | Boolean有兩種值true和false |
String | 字符串類型 |
Number | 數(shù)字類型,包括整數(shù)和浮點(diǎn)數(shù) |
Object | 它是一系列屬性的無序集合,包括函數(shù)Function和數(shù)組Array |
使用typeof是無法判斷function和array的,使用instanceof在多個(gè)iframe里也會出錯(cuò),我們可以使用Object.prototype.toString方法,它可判斷出各種類型。
默認(rèn)情況下,每個(gè)對象都會從Object上繼承到toString()方法,如果這個(gè)方法沒有被這個(gè)對象自身或者更接近的上層原型上的同名方法覆蓋(遮蔽),則調(diào)用該對象的toString()方法時(shí)會返回[object type],這里的字符串type表示了一個(gè)對象類型。
我們先寫一個(gè)type函數(shù),用于接下來的深復(fù)制時(shí)判斷類型:
function type(obj) { var toString = Object.prototype.toString; var map = { '[object Boolean]' : 'boolean', '[object Number]' : 'number', '[object String]' : 'string', '[object Function]' : 'function', '[object Array]' : 'array', '[object Date]' : 'date', '[object RegExp]' : 'regExp', '[object Undefined]': 'undefined', '[object Null]' : 'null', '[object Object]' : 'object' }; return map[toString.call(obj)]; }
現(xiàn)在已經(jīng)很清楚了,如何實(shí)現(xiàn)深復(fù)制呢,對于非引用類型的數(shù)值,直接賦值,而對于引用類型的值(object)需要多次遍歷,遞歸復(fù)制。
function deepClone(data) { var t = type(data), o, i, ni; if(t === 'array') { o = []; }else if( t === 'object') { o = {}; }else { return data; } if(t === 'array') { for (i = 0, ni = data.length; i < ni; i++) { o.push(deepClone(data[i])); } return o; }else if( t === 'object') { for( i in data) { o[i] = deepClone(data[i]); } return o; } }
這里有個(gè)點(diǎn)大家要注意下,對于function類型,這里是直接賦值的,還是共享一個(gè)內(nèi)存值。這是因?yàn)楹瘮?shù)更多的是完成某些功能,有個(gè)輸入值和返回值,而且對于上層業(yè)務(wù)而言更多的是完成業(yè)務(wù)功能,并不需要真正將函數(shù)深拷貝。
淺拷貝,對于淺拷貝而言,可以理解為只操作一個(gè)共同的內(nèi)存區(qū)域!這里會存在危險(xiǎn)!
如果直接操作這個(gè)共享的數(shù)據(jù),不做控制的話,會經(jīng)常出現(xiàn)數(shù)據(jù)異常,被其它部分更改。所以應(yīng)該不要直接操作數(shù)據(jù)源,給數(shù)據(jù)源封裝一些方法,來對數(shù)據(jù)來進(jìn)行CURD操作。
到這里估計(jì)就差不多了,但是作為一個(gè)前端,不僅僅考慮javascript本身,還得考慮到dom、瀏覽器等。
Element類型,來看下面代碼,結(jié)果會返回啥呢?
Object.prototype.toString.call(document.getElementsByTagName('div')[0])
經(jīng)過測試,返回的是:[object HTMLDivElement]
有時(shí)候保存了dom元素, 一不小心進(jìn)行深拷貝,上面的深拷貝函數(shù)就缺少了對Element元素的判斷。而判斷Element元素要使用instanceof來判斷。因?yàn)閷τ诓煌臉?biāo)簽,toString會返回對應(yīng)不同的標(biāo)簽的構(gòu)造函數(shù)。
function type(obj) { var toString = Object.prototype.toString; var map = { '[object Boolean]' : 'boolean', '[object Number]' : 'number', '[object String]' : 'string', '[object Function]' : 'function', '[object Array]' : 'array', '[object Date]' : 'date', '[object RegExp]' : 'regExp', '[object Undefined]': 'undefined', '[object Null]' : 'null', '[object Object]' : 'object' }; if(obj instanceof Element) { return 'element'; } return map[toString.call(obj)]; }
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
相關(guān)文章
小程序使用watch監(jiān)聽數(shù)據(jù)變化的方法詳解
這篇文章主要介紹了小程序使用watch監(jiān)聽數(shù)據(jù)變化的方法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09avalonjs實(shí)現(xiàn)仿微博的圖片拖動特效
JavaScript實(shí)現(xiàn)仿微博的圖片拖動特效,貌似這些天有不少朋友需要這功能,今天發(fā)現(xiàn)這款是js制作的好,不敢獨(dú)享,希望需要的朋友喜歡哦。2015-05-05javascript數(shù)據(jù)結(jié)構(gòu)與算法之檢索算法
查找數(shù)據(jù)有2種方式,順序查找和二分查找。順序查找適用于元素隨機(jī)排列的列表。二分查找適用于元素已排序的列表。二分查找效率更高,但是必須是已經(jīng)排好序的列表元素集合2015-04-04JavaScript 格式化數(shù)字、金額、千分位、保留幾位小數(shù)、舍入舍去
這篇文章主要介紹了JavaScript 格式化數(shù)字、金額、千分位、保留幾位小數(shù)、舍入舍去,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07JavaScript原生開發(fā)視頻播放器的實(shí)現(xiàn)代碼
這篇文章我們將一起探索一份自定義的視頻播放器實(shí)現(xiàn)代碼,甚至還可以實(shí)現(xiàn)有彈幕功能,文中的示例代碼講解詳細(xì),感興趣的可以了解一下2023-06-06