javascript中的深復制
JavaScript深拷貝是初學者甚至有經驗的開發著,都會經常遇到問題,并不能很好的理解javascript的深拷貝。
深拷貝(deepClone)是神馬,與深拷貝相對應的就是淺拷貝,剛開始我也沒弄懂。
在很多情況下,我們都需要給變量賦值,給內存地址賦予一個值,但是在賦值引用值類型的時候,只是共享一個內存區域,導致賦值的時候,還跟之前的值保持一直性。
看一個具體的例子
// 給test賦值了一個對象var test = { a: 'a', b: 'b'};// 將test賦值給test2// 此時test和test2是共享了同一塊內存對象,這也就是淺拷貝var test2 = test;test2.a = 'a2';test.a === 'a2'// 為true如下圖:

這下就很好理解為什么引用值類型數據相互影響問題。
實現一個深拷貝函數,就不得不說javascript的數值類型。下面我們先來看一個js有哪幾種數據類型:
| 類型 | 描述 |
|---|---|
| undefined | undefined類型只有一個值undefined,它是變量未被賦值時的值 |
| null | null類型也只有一個值,它是一個空對象的引用 |
| Boolean | Boolean有兩種值true和false |
| String | 字符串類型 |
| Number | 數字類型,包括整數和浮點數 |
| Object | 它是一系列屬性的無序集合,包括函數Function和數組Array |
使用typeof是無法判斷function和array的,使用instanceof在多個iframe里也會出錯,我們可以使用Object.prototype.toString方法,它可判斷出各種類型。
默認情況下,每個對象都會從Object上繼承到toString()方法,如果這個方法沒有被這個對象自身或者更接近的上層原型上的同名方法覆蓋(遮蔽),則調用該對象的toString()方法時會返回[object type],這里的字符串type表示了一個對象類型。
我們先寫一個type函數,用于接下來的深復制時判斷類型:
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)];}現在已經很清楚了,如何實現深復制呢,對于非引用類型的數值,直接賦值,而對于引用類型的值(object)需要多次遍歷,遞歸復制。
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; }}這里有個點大家要注意下,對于function類型,這里是直接賦值的,還是共享一個內存值。這是因為函數更多的是完成某些功能,有個輸入值和返回值,而且對于上層業務而言更多的是完成業務功能,并不需要真正將函數深拷貝。
淺拷貝,對于淺拷貝而言,可以理解為只操作一個共同的內存區域!這里會存在危險!
如果直接操作這個共享的數據,不做控制的話,會經常出現數據異常,被其它部分更改。所以應該不要直接操作數據源,給數據源封裝一些方法,來對數據來進行CURD操作。
到這里估計就差不多了,但是作為一個前端,不僅僅考慮javascript本身,還得考慮到dom、瀏覽器等。
Element類型,來看下面代碼,結果會返回啥呢?
Object.prototype.toString.call(document.getElementsByTagName('div')[0])經過測試,返回的是:[object HTMLDivElement]
有時候保存了dom元素, 一不小心進行深拷貝,上面的深拷貝函數就缺少了對Element元素的判斷。而判斷Element元素要使用instanceof來判斷。因為對于不同的標簽,toString會返回對應不同的標簽的構造函數。
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)];}感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
新聞熱點
疑難解答