【轉】JavaScript 對象的深度克隆

該文轉載自http://www.cnblogs.com/zichi/p/4568150.html,有部分修改。html

在聊JavaScript(如下簡稱js)深度克隆以前,咱們先來了解一下js中對象的組成。
js 中一切實例皆是對象,具體分爲 原始類型合成類型
原始類型 對象指的是 UndefinedNullBooleanNumberString ,按值傳遞。
合成類型 對象指的是 arrayobject 以及 function ,按址傳遞,傳遞的時候是內存中的地址。 數組

克隆或者拷貝分爲2種: 淺度克隆深度克隆
淺度克隆 :基本類型爲值傳遞,對象仍爲引用傳遞。
深度克隆 :全部元素或屬性均徹底克隆,並於原引用類型徹底獨立,即,在後面修改對象的屬性的時候,原對象不會被修改。 函數

又或許你剛據說「深度克隆」這個詞,簡單來講,就是說有個變量a,a的值是個對象(包括基本數據類型),如今你要建立一個變量b,使得它擁有跟a同樣的方法和屬性等等。可是a和b之間不能相互影響,即a的值的改變不影響b值的變化。直接賦值可好?測試

var a = 1;
var b = a;
a = 10;
console.log(b);            // 1
 
var a = 'hello';
var b = a;
a = 'world';
console.log(b);            // hello
 
var a = true;
var b = a;
a = false;
console.log(b);            // true

實踐證實某些 JavaScript 的原始數據類型,若是要克隆直接賦值便可。
關於 function 的深度複製:查閱了一些資料, function 的深度複製彷佛和原始數據類型的深度複製同樣。.net

var a = function () {
    console.log(1);
};
var b = a;
a = function () {
    console.log(2);
};
b();                    // 1

原本我也是這麼認爲的,直到文章下出現了評論。思考後我以爲 function 和普通的對象同樣,只是咱們在日常應用中習慣了總體的從新賦值,致使它在深度複製中的表現和原始類型一致:prototype

var a = function () {
    console.log(1);
};
a.tmp = 10;
var b = a;
a.tmp = 20;
console.log(b.tmp);        // 20

因而乎對於 function 類型的深度克隆,直接賦值彷佛並不該該是一種最好的方法(儘管實際應用中足矣)。code

可是對象呢?htm

var a = [0,1,2,3];
var b = a;
a.push(4);
console.log(b);            // [0, 1, 2, 3, 4]

顯然與預期不符,爲何會這樣?由於原始數據類型儲存的是對象的實際數據,而對象類型存儲的是對象的引用地址。上面的例子呢也就是說a和b對象引用了同一個地址,不管改變a仍是改變b,其實根本操做是同樣的,都是對那塊空間地址中的值的改變。對象

因而咱們知道了,對於基本的對象來講,不能只能用 「 = 」 賦值,思索後寫下以下代碼:blog

// 判斷arr是否爲一個數組,返回一個bool值
function isArray (arr) {
    return Object.prototype.toString.call(arr) === '[object Array]';  
}
// 深度克隆
function deepClone (obj) {  
    if(typeof obj !== "object" && typeof obj !== 'function') {
        return obj;        //原始類型直接返回
    }
    var o = isArray(obj) ? [] : {}; 
    for(i in obj) {  
        if(obj.hasOwnProperty(i)){ 
            o[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i]; 
        } 
    } 
    return o;
}

注意代碼中判斷數組的時候用的不是 obj instanceof Array ,這是由於該方法存在一些小問題,詳情見 http://www.nowamagic.net/librarys/veda/detail/1250

用一些代碼來測試下:

// 測試用例:
var srcObj = {
    a: 1,
    b: {
        b1: ["hello", "hi"],
        b2: "JavaScript"
    }
};
var abObj = srcObj;
var tarObj = cloneObject(srcObj);

srcObj.a = 2;
srcObj.b.b1[0] = "Hello";

console.log(abObj.a);
console.log(abObj.b.b1[0]);

console.log(tarObj.a);      // 1
console.log(tarObj.b.b1[0]);    // "hello"

彷佛能夠解決通常的對象(包括 Array )的深度克隆了,或許這兒會有疑問,new String(..) 這類的也是對象啊,但是這樣寫你克隆不了啊…可是樓主以爲深度克隆的考點不在這裏,可能在於:

  • 原始數據類型的直接賦值

  • function的exception

  • 對象的深度克隆中Array類型的判斷

  • 克隆函數的遞歸調用

相關文章
相關標籤/搜索