JavaScript中的淺拷貝與深拷貝


  學了這麼長時間的JavaScript想必大家對淺拷貝和深拷貝還不太熟悉吧,今天在項目中既然用到了,早晚也要理清一下思路了,在瞭解之前,我們還是先從JavaScript的數據類型存放的位置 堆棧開始說起吧!           

現在我們帶着問題來學習!

一:什麼是堆棧?

  我們都知道:在計算機領域中,堆棧是兩種數據結構,它們只能在一端(稱爲棧頂(top))對數據項進行插入和刪除。

  • 堆:隊列優先,先進先出;由操作系統自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧。
  • 棧:先進後出;動態分配的空間 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收,分配方式倒是類似於鏈表。 
    以上都屬於計算機基礎部分,在此都不詳細贅述了,下面我們聯繫JavaScript來剖析一下堆棧。

二:JavaScript中的基本類型和引用類型與堆棧有什麼聯繫?

JavaScript的數據類型分爲兩大種: 
1. 基本類型:Undefined、Null、Boolean、Number 和 String,這5中基本數據類型可以直接訪問,他們是按照值進行分配的,存放在棧(stack)內存中的簡單數據段,數據大小確定,內存空間大小可以分配。 
2. 引用類型:即存放在堆(heap)內存中的對象,變量實際保存的是一個指針,這個指針指向另一個位置。 
  以上我們知道了什麼是堆棧,和JavaScript的數據類型,下面我們根據js的數據類型來說明一下他們的拷貝情況:

var obj1 = {name:'bangbang',age:18};
var b = obj1;
var c = obj1.age;

console.log(b.name); //bangbang
console.log(c);      //18
//改變b和c的值
b.name = 'yanniu';
c = 22;
console.log(obj1.name);     //yanniu
console.log(obj1.age);       //18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

  以上看出:當我們改變b的數據的時候,我們看到了obj1.name的數據也在改變,但是我們改變c的數據的時候發現,obj1.age的值沒有變化,這說明了:b和obj1變量操作的是同一個對象,c和obj1完全獨立的。圖示如下: 
這裏寫圖片描述

三:什麼是淺拷貝?

  根據上面的陳述,基本類型拷貝的時候只是在內存中又開闢了新的空間,和它的父元素(再次我們稱被拷貝的對象爲父元素)屬於 互不想幹的東西,因此深淺拷貝是相對於引用類型的,以便於我們對引用類型父對象的保存! 嘿嘿!我們接着看! 
例如:

var father1 = {name:'shangdi',age:1000,job:['teacher','cook']};
//淺拷貝函數
function copy(obj){
    var childs = {};
    for(var key in obj){
        childs[key] = obj[key];
    }
    return childs;
}
var child1 = copy(father1);
console.log(child1);    //{ name: 'shangdi', age: 1000 }
console.log(typeof child1); //object

//改變子對象的name屬性,發現對父對象的name沒有影響 哈哈!
child1.name = 'bangbang';
console.log(father1);   //{ name: 'shangdi', age: 1000 }
console.log(child1);    //{ name: 'bangbang', age: 1000 }

//注意:這次改變子對象的job屬性也就是改變數組,
//發現對父對象的job竟然有影響,嚇死寶寶了,那怎麼辦呢,那這個copy有什麼用呢是吧!
child1.job.push('programer');
console.log(father1);   //{ name: 'shangdi',age: 1000,job: [ 'teacher', 'cook', 'programer' ] }
console.log(child1);    //{ name: 'shangdi',age: 1000,job: [ 'teacher', 'cook', 'programer' ] }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

由上面可分析:淺拷貝的時候,當我們改變子對象的數組的時候,父對象竟然也跟着改變,也就是說:子對象和父對象在淺拷貝的時候他們指向同一個內存的數組:由圖所示: 
這裏寫圖片描述 
如果我們想讓子對象的拷貝和父對象沒有一點關聯,那麼我們就必須用到深度拷貝!嘿嘿!兒子也不能完全跟着爹長啊!

四:什麼是深度拷貝?

深度拷貝就是把父對象拷貝到子對象上,而且兩者的內存和以後的操作都互不影響的拷貝!

        function deepCopy(obj){
            var o;
            switch(typeof obj){
            case 'undefined': break;
            case 'string'   : o = obj + '';break;
            case 'number'   : o = obj - 0;break;
            case 'boolean'  : o = obj;break;
            case 'object'   :
                if(obj === null){
                    o = null;
                }else{
                    if(obj instanceof Array){
                        o = [];
                        for(var i = 0, len = obj.length; i < len; i++){
                            o.push(deepCopy(obj[i]));
                        }
                    }else{
                        o = {};
                        for(var k in obj){
                            o[k] = deepCopy(obj[k]);
                        }
                    }
                }
                break;
            default:
                o = obj;break;
            }
            return o;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

這裏寫圖片描述 
   下面是一些克隆的方法供大家參考,不過他們有區別,自己試驗: 
方法二:最簡單的

    function deepCopy(obj) {
        return JSON.parse(JSON.stringify(obj));
    }
  • 1
  • 2
  • 3

方法三:

        function deepCopy(obj){
            var newobj, obj;
            if (obj.constructor == Object){
                newobj = new obj.constructor();
            }else{
                newobj = new obj.constructor(obj.valueOf());//valueOf()方法返回 Array 對象的原始值
            }
            for(var key in obj){
                if ( newobj[key] != obj[key] ){
                    if ( typeof(obj[key]) == 'object' ){
                        newobj[key] = deepCopy(obj[key]);
                    }else{
                        newobj[key] = obj[key];
                    }
                }
            }
            newobj.toString = obj.toString;
            newobj.valueOf = obj.valueOf;
            return newobj;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

方法四:

var cloneObj = function(obj){
    var str, newobj = obj.constructor === Array ? [] : {};
    if(typeof obj !== 'object'){
        return;
    } else if(window.JSON){
        str = JSON.stringify(obj), //系列化對象
        newobj = JSON.parse(str); //還原
    } else {
        for(var i in obj){
            newobj[i] = typeof obj[i] === 'object' ? 
            cloneObj(obj[i]) : obj[i]; 
        }
    }
    return newobj;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

方法五:(JavaScript面向對象編程指南)

    function deepCopy(p,c){
        c = c || {};
        for (var i in p){
            if(p.hasOwnProperty(i)){
                if(typeof p[i] === 'object'){
                    c[i] = Array.isArray(p[i]) ? [] : {};
                    deepCopy(p[i],c[i]);
                }else{
                    c[i] = p[i];
                }
            }
        }
        return c;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章