JavaScript對象的深淺複製

前言

從層次上來看,對象的複製能夠簡單地分爲淺複製和深複製,顧名思義,淺複製是指只複製一層對象的屬性,不會複製對象中的對象的屬性,對象的深複製會複製對象中層層嵌套的對象的屬性。
在複製對象時,除了要複製對象的屬性外,還要兼顧到是否保留了對象的constructor屬性,是否對每一種數據類型(JavaScript常見的數據類型有String,Number,Boolean,Data,RegExp,Array,Funtion,Object)都實現正確的複製。項目中,咱們能夠根據實際狀況,決定須要實現什麼樣程度的複製。
本文是我在複製對象方面的一些心得總結,由淺複製到深複製,由只複製簡單屬性到複製Function,RegExp等複雜屬性,層層遞進。若有陳述不當之處,煩請指出,不勝感激。git

正文

淺複製

淺複製只會依次複製對象的每個屬性,不會對這些屬性進行遞歸複製。下面是一個簡單的淺複製實現。github

//對象淺複製                                                           
function shadowCopy(obj){
        if(typeof obj !== 'object') return obj;

        for(var prop in obj){
            if(obj.hasOwnProperty(prop)){
                newObj[prop] = obj[prop];
            }
        }
        return newObj;
    }

仔細觀察,不難發現上述方法的缺陷:
1.不能正確實現數組的淺複製
2.複製操做丟失了對象的constructor屬性編程

好,咱們如今已經發現了問題所在,只需針對性地解決,一個還算完美的淺複製對象的方法就誕生了!數組

//對象淺複製
    function shadowCopy(obj){
            if(typeof obj !== 'object') return ;
            var newObj;

            //保留對象的constructor屬性
            if(obj.constructor === Array){
                newObj = [];
            } else {
                newObj = {};
                newObj.constructor = obj.constructor;
            }

            for(var prop in obj){
                if(obj.hasOwnProperty(prop)){
                    newObj[prop] = obj[prop];
                }
            }
            return newObj;
        }

瀏覽器中測試一下:瀏覽器

var arr1 = [0,1,2];
    console.log(arr1);
    console.log(shadowCopy(arr1));
    
    var arr2 = [0,1,2,[3,4,5]],
        arr2Copy = shadowCopy(arr2);
    console.log(arr2);
    console.log(arr2Copy);
    arr2Copy[3][0] = 6;
    console.log(arr2[3][0]);  //6

Good! 能夠正確實現數組複製和而且保留constructor了,但細心的你必定發現了,淺複製後的對象的 arr2Copy[3]arr2[3] 指向的是一個對象,改變其中一個,同時也會改變另外一個。咱們想要實現的是 複製,但這並非複製呀!
這是淺複製的一個弊端所在,接下讓咱們看看深複製是怎樣解決這個問題的。編程語言

深複製

深複製須要層層遞歸,複製對象的全部屬性,包括對象屬性的屬性的屬性....(暈~)
若是隻是須要簡單地複製對象的屬性,而不用考慮它的constructor,也不用考慮函數,正則,Data等特殊數據類型,那這裏有一個深複製的小trick,兩行代碼便可:函數

function deepCopy(obj){
    if(typeof obj !== "object"){ return ;}
    var str = JSON.stringify(obj);
    return JSON.parse(str);
}

大多數狀況下,上面的就能夠知足要求了,但一些時候,咱們須要把函數,正則等特殊數據類型也考慮在內,或者當前環境不支持JSON時,上面的方法也就不適用了。這時,咱們能夠經過遞歸來實現對象的深層複製,以下:學習

function deepCopy(obj){
    if(typeof obj !== "object"){ return ;}
    var newObj;

    //保留對象的constructor屬性
    if(obj.constructor === Array){
        newObj = [];
    } else {
        newObj = {};
        newObj.constructor = obj.constructor;
    }

    for(var prop in obj){
        if(typeof obj[prop] === 'object'){
            if(obj[prop].constructor === RegExp ||obj[prop].constructor === Date){
                newObj[prop] = obj[prop];
            } else {
                //遞歸
                newObj[prop] = deepCopy(obj[prop]);
            }
        } else {
            newObj[prop] = obj[prop];
        }
    }
    return newObj;
}

先用上面的例子測試:測試

棒!能夠正確實現多維數組的複製,再看是否能實現函數和正則的複製:this

function Person(name){
    this.name = name;
    this.age = age;
    this.search = new RegExp(name);
    this.say = function(){
        console.log(this.name + "今年" + this.age + "歲了");
    }
}
var p1 = new Person("Claiyre",20),
    p2 = deepCopy(p1);

console.log(p1);
console.log(p2);

p2.age = 22;
p1.say();
p2.say();

圓滿完成!!

稍加整理,咱們就能夠獲得一個較爲通用的js對象複製函數:

function deepCopy(obj){
    var newObj = obj.constructor === Array ? []:{};
    newObj.constructor = obj.constructor;

    if(typeof obj !== "object"){ 
        return ;
    } else if(window.JSON){
        //若須要考慮特殊的數據類型,如正則,函數等,需把這個else if去掉便可
        newObj = JSON.parse(JSON.stringify(obj));
    } else {
        for(var prop in obj){
            if(obj[prop].constructor === RegExp ||obj[prop].constructor === Date){
                newObj[prop] = obj[prop];
            } else if(typeof obj[prop] === 'object'){
                //遞歸
                newObj[prop] = deepCopy(obj[prop]);
            } else {
                newObj[prop] = obj[prop];
            }
        }
    } 
    return newObj;
}

結語

面向對象的編程語言,其核心是對象,所以深刻了解對象的相關操做,縱向比較異同,對學習過程是極有好處的。

博客原文地址:Claiyre的我的博客 https://claiyre.github.io/
博客園地址:http://www.cnblogs.com/nuannuan7362/如需轉載,請在文章開頭註明原文地址

相關文章
相關標籤/搜索