1. 程式人生 > >淺拷貝和深拷貝的區別和實現方法

淺拷貝和深拷貝的區別和實現方法

對於這個問題可以從深拷貝和淺拷貝的使用或起源說起。

1. js變數包含兩種不同資料型別的值基本型別和引用型別

基本型別包括ES6新增的一共是6種,具體如下

string ,number, null ,undefined, boolean ,symbol

引用型別為那些可能由多個值構成的物件,只有一種

object

將一個值賦給變數時,解析器必須先確定這個值是引用型別還是基本型別。 基本資料型別可以操作儲存在變數中的實際的值 引用型別的值是儲存在記憶體中的值,但是JavaScript不允許直接訪問記憶體中的位置,就是說不能直接操作物件的記憶體空間。在操作物件時,我們實際上操作的是物件的引用而不是實際的值。

2. 變數儲存方式–棧和堆

棧:自動分配記憶體空間,自動釋放,裡面存放的是基本型別的值和引用型別的地址

堆:動態分配的記憶體,大小不定,不會自動釋放。裡面存放引用型別的值。 變數儲存方式

3. 值傳遞和址傳遞

基本型別和引用型別最大的區別就是傳值和傳址的區別

基本型別採用值傳遞

var a = 100;
var b = a;
b++;
console.log(a);  //100
console.log(b);  //101

引用型別則採用址傳遞,將存放在棧中的地址賦值給變數

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

這裡我們本意只想給陣列b新增一個元素,然而陣列a也增加了。 分析: 由於a和b都是引用型別,採用址傳遞,賦值的時候是把a的地址賦值給b,即a和b指向同一個地址,而這個地址都指向堆記憶體中引用型別的值,當b改變這個值的同時,因為a的地址也指向這個值,所以a的值也跟著變化。

要解決上面出現的問題,就是要使用深拷貝或者淺拷貝了。

首先,淺拷貝和深拷貝都只針對於array、object這種複雜物件。

淺拷貝解決就是設定一個新的物件obj2,通過遍歷的方式將obj1物件的值一一賦值給obj2物件。

以下為淺拷貝的實現,比較簡單,其實就是遍歷物件屬性的問題


//陣列的淺拷貝

var a = [1, 2, 3];
var b = [];
for(var i in a) {
	b[i] = a[i];
}
b.push(4);
console.log(a); // [1,2,3]
console.log(b); // [1,2,3,4]


//物件的淺拷貝

var obj1 = {
	''a:1,
	'b':2
};
var obj2 = {};
for (var i in obj1) {
	obj2[i] = obj[i];
}
obj2['c'] = 3;

console.log(obj1); // {a: 1, b: 2  }
console.log(obj2); // {a:1, b: 2, c: 3 }


但上面的程式碼只能進行一層拷貝,無法進行深層次的拷貝,封裝函式後對陣列的巢狀進行測試

function shallowClone (source) {

	var target = {};
	for (var i in source) {
		if(source.hasOwnProperty (i) ) {
			target[i] = source[i];
		}
	}
	return target;
}

var obj1 = {
	fruits: ['apple','banner'],
	num: 100
}
var obj2  = shallowClone(obj1)

obj2.fruits[0] = 'orange'
console.log(obj1.fruits[0]); //orange
console.log(obj2.fruits[0]); //orange

結果證明無法進行深層次的拷貝,這個時候我們可以使用深拷貝來完成

以下為實現深拷貝的方法

深拷貝的問題其實可以分解成兩個問題,淺拷貝+遞迴,什麼意思呢? 看下面三個實現方法

  1. 用for…in實現遍歷和複製
function deepClone(obj){
    let objClone = Array.isArray(obj)?[]:{};
    if(obj && typeof obj==="object"){
        for(key in obj){
            if(obj.hasOwnProperty(key)){
                //判斷ojb子元素是否為物件,如果是,遞迴複製
                if(obj[key]&&typeof obj[key] ==="object"){
                    objClone[key] = deepClone(obj[key]);
                }else{
                    //如果不是,簡單複製
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}    
let a=[1,2,3,4],
    b=deepClone(a);
a[0]=2;
console.log(a,b);
  1. JSON方法實現
let deepClone = function (obj) {
	    	let  temp = JSON.stringify(obj);
		    let result  = JSON.parse(temp);
		    return result;
}
let obj1 = {
    a: {
        age: 20,
        class: 1602
    },
    b: {
        age: 21,
        class: 1601
    }
};
let obj2 = deepClone(obj1);
console.log(obj2);
  1. 利用陣列的Array.prototype.forEach進copy

let deepClone = function (obj) {
    let copy = Object.create(Object.getPrototypeOf(obj));
    let propNames = Object.getOwnPropertyNames(obj);
    propNames.forEach(function (items) {
        let item = Object.getOwnPropertyDescriptor(obj, items);
        Object.defineProperty(copy, items, item);

    });
    return copy;
};

let testObj = {
    name: "weiqiujuan",
    sex: "girl",
    age: 22,
    favorite: "play",
    family: {brother: "wei", mother: "haha", father: "heihei"}
}
let testRes2 = deepClone(testObj);
console.log(testRes2);