1. 程式人生 > 其它 >深拷貝和淺拷貝的認識以及物件深拷貝的封裝

深拷貝和淺拷貝的認識以及物件深拷貝的封裝

技術標籤:javascript深拷貝與淺拷貝js

故心故心故心故心小故衝啊


文章目錄


前言

什麼是深拷貝和淺拷貝,請舉例說明?如何區分深拷貝與淺拷貝,簡單來說,假設B複製了A,當修改A時,看B是否會發生變化,如果B也跟著變了,說明這是淺拷貝,如果B沒變,那就是深拷貝;
我們先看兩個簡單的案例:

//案例1 深拷貝 
var a1 = 1, a2= a1;
console.log(a1) //1
console.log(a2) //1
a2 = 2; //修改 a2
console.log(a1) //1
console.
log(a2) //2
//案例2 淺拷貝 跟這變
var o1 = {x: 1, y: 2}, o2 = o1;
console.log(o1) //{x: 1, y: 2}
console.log(o2) //{x: 1, y: 2}
o2.x = 2; //修改o2.x
console.log(o1) //{x: 2, y: 2}
console.log(o2) //{x: 2, y: 2}

按照常規思維,o1應該和a1一樣,不會因為另外一個值的改變而改變,而這裡的o1 卻隨著o2的改變而改變了。同樣是變數,為什麼表現不一樣呢?為了更好的理解js的深淺拷貝,我們先來理解一些js基本的概念 —— 目前JavaScript有五種基本資料型別(也就是簡單資料型別),它們分別是:Undefined,Null,Boolean,Number和String。還含有一種複雜的資料型別(也叫引用型別),就是物件,引用型別有:Object、Array、Function(之所以說“目前”,因為之後也可能會有新的型別出來。)

一、基本型別和引用型別

ECMAScript變數可能包含兩種不同資料型別的值:基本型別值和引用型別值。基本型別值指的是那些儲存在棧記憶體中的簡單資料段,即這種值完全儲存在記憶體中的一個位置。而引用型別值是指那些儲存堆記憶體中的物件,意思是變數中儲存的實際上只是一個指標,這個指標指向記憶體中的另一個位置,該位置儲存物件。

打個比方,基本型別和引用型別在賦值上的區別可以按“雙胞胎”和“影子”來理解:基本型別賦值等於一位媽媽生的雙胞胎,二者互不相關,各自有各自的特性;而引用型別賦值相當於自己跟影子,你在變化時影子會隨著變化;

上面清晰明瞭的介紹了基本型別和引用型別的定義和區別。

再回到前面的案例,案例1中的值為基本型別,案例2中的值為引用型別。案例2中的賦值就是典型的淺拷貝,並且深拷貝與淺拷貝的概念只存在於引用型別

二、深拷貝與淺拷貝

既然已經知道了深拷貝與淺拷貝的來由,那麼該如何實現深拷貝?我們先分別看看Array和Object自有方法是否支援:

1、Array

  1. slice
var arr1 = ['a', 'b'], arr2 = arr1.slice();
console.log(arr1); // ["a", "b"]
console.log(arr2); // ["a", "b"]
arr2[0] = 'c'; //修改arr2
console.log(arr1); // ["a", "b"]
console.log(arr2); // ["c", "b"]
  1. concat
var arr1 = ['a', 'b'], arr2 = arr1. concat ();
console.log(arr1); // ["a", "b"]
console.log(arr2); // ["a", "b"]
arr2[0] = 'c'; //修改arr2
console.log(arr1); // ["a", "b"]
console.log(arr2); // ["c", "b"]

把arr1改成二維陣列再來看看:

var arr1 = ['a', 'b', ['c', 'd']], arr2 = arr1.concat();
arr2[2][1] = 100; 
console.log(arr1); //['a', 'b', ['c', 100]]
console.log(arr2); //['a', 'b', ['c', 100]]
//arr2又改變了arr1,看來slice()/concat()只能實現一維陣列的深拷貝

2、Object

  1. 利用物件的深拷貝實現原理

定義一個新的物件,遍歷源物件的屬性 並 賦給新物件的屬性

var obj = {
   name:'sonia',
   age: 18
}
var obj2 = new Object();
obj2.name = obj.name;
obj2.age = obj.age

obj.name = 'alice';
console.log(obj); //Object {name: "'alice'", age: 18}
console.log(obj2); //Object {name: "'sonia'", age: 18}

封裝一個方法 deep來實現物件的深拷貝,程式碼如下

var Animal={
    name: "duobi",
    skin: ["red", "green"],
    child: {
        xxx: "xxx"
    },
    say: function(){
        console.log("I am ", this.name, " skin:", this.skin)
    }
}
function deep(dest, obj){
    var o = dest;
    for (var key in obj) {  
        if (typeof obj[key] === 'object'){   //判斷是不是物件
            o[key] = (obj[key].constructor===Array)?[]:{};
            deep(o[key], obj[key])
        } else {
            o[key] = obj[key]
        }
    }
    return o;
}
//序列化的深拷貝(函式不支援)
JSON.parse(JSON.stringify(obj))

var obj = {
    name: 'sonia',
    age: 18
}
var obj2 = JSON.parse(JSON.stringify(obj));

obj.name = 'alice';
console.log(obj) // {name: "alice", age: 18}
console.log(obj2) // {name: "sonia", age: 18}