javascript深拷貝(deepClone)

javascript深拷貝是初學者甚至有經驗的開發着,都會常常遇到問題,並不能很好的理解javascript的深拷貝。javascript

深拷貝(deepClone)?

與深拷貝相對的就是淺拷貝,不少初學者在接觸這個感念的時候,是很懵逼的。
html

爲啥要用深拷貝?

在不少狀況下,咱們都須要給變量賦值,給內存地址賦予一個值,可是在賦值引用值類型的時候,只是共享一個內存區域,致使賦值的時候,還跟以前的值保持一直性。
看一個具體的例子前端

// 給test賦值了一個對象
var test = {
    a: 'a',
    b: 'b'
};

// 將test賦值給test2
// 此時test和test2是共享了同一塊內存對象,這也就是淺拷貝
var test2 = test;

test2.a = 'a2';

test.a === 'a2'// 爲true

圖解:
java

這下就很好理解爲何引用值類型數據相互影響問題。jquery

實現

實現一個深拷貝函數,就不得不說javascript的數值類型。git

判斷javascript類型

javascript中有如下基本類型github

類型 描述
undefined undefined類型只有一個值undefined,它是變量未被賦值時的值
null null類型也只有一個值null, 它是一個空的對象引用
Boolean Boolean有兩種取值true和false
String 它表示文本信息
Number 它表示數字信息
Object 它是一系列屬性的無序集合, 包括函數Function和數組Array

使用typeof是沒法判斷function和array的,這裏使用Object.prototype.toString方法。
[默認狀況下,每一個對象都會從Object上繼承到toString()方法,若是這個方法沒有被這個對象自身或者更接近的上層原型上的同名方法覆蓋(遮蔽),則調用該對象的toString()方法時會返回"[object type]",這裏的字符串type表示了一個對象類型][1]數組

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)];
}

實現deepClone

對於非引用值類型的數值,直接賦值,而對於引用值類型(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類型,博主這裏是直接賦值的,仍是共享一個內存值。這是由於函數更多的是完成某些功能,有個輸入值和返回值,並且對於上層業務而言更多的是完成業務功能,並不須要真正將函數深拷貝。dom

可是function類型要怎麼拷貝呢?

其實博主只想到了用new來操做一下,可是function就會執行一遍,不敢想象會有什麼執行結果哦!o(╯□╰)o!其它暫時尚未什麼好的想法,歡迎你們指導哦!

到這裏差很少也就實現完了深拷貝,又有人覺的怎麼沒有實現淺拷貝呢?

淺拷貝?

對於淺拷貝而言,能夠理解爲只操做一個共同的內存區域!這裏會存在危險!(。﹏。*)

若是直接操做這個共享的數據,不作控制的話,會常常出現數據異常,被其它部分更改。因此應該不要直接操做數據源,給數據源封裝一些方法,來對數據來進行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)];
}

其它方式?

  1. jquery的實現
    詳見https://github.com/jquery/jqu...

  2. underscore的實現
    詳見https://github.com/jashkenas/...

  3. lodash的實現
    詳見https://github.com/lodash/lod...

  4. JSON實現
    先經過JSON.stringify一下,而後再JSON.parse一下,就能實現深拷貝。可是數據類型只支持基本數值類型。

var obj = {
    a: 'a',    
    b: function(){console.log('b')}
}

//在JSON.stringify的時候就會把function給過濾了。

JSON.stringify(obj)// "{"a":"a"}"

小結

這裏大概總結了一下深拷貝,以及怎麼實現一個深拷貝。在不一樣的場景下,要根據業務場景,判斷是否須要使用深拷貝。
原文連接 http://xiaoqiang730730.github...

參考文獻

winter-JavaScript中的類型
http://www.cnblogs.com/winter...

相關文章
相關標籤/搜索