1. 程式人生 > >深入理解Javascript中的堆與棧、淺拷貝與深拷貝

深入理解Javascript中的堆與棧、淺拷貝與深拷貝

Javascript中的淺拷貝與深拷貝
先從JavaScript的資料型別存放的位置堆疊開始說吧
什麼是堆疊?
我們知道計算機領域中堆疊是兩種資料結構,它們只能再一端(稱為棧頂(top))對資料項進行插入和刪除。
堆:佇列優先,先進先出,由作業系統自動分配釋放,存放函式的引數值,區域性變數的值等
棧:先進後出;動態分配的空間一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由OS回收,分配的方式倒是類似於連結串列
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);
console.log(c);
//改變b和c的值
b.name='yanniu';
c=22;
console.log(obj1.name)
; //yanniu console.log(obj1.age); //18

以上看出:當我們改變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} conosle.log(typeof child1);//object //注意:這次改變子物件的job屬性也就是改變陣列, //發現對父物件的job竟然又影響,嚇死寶寶了,那怎麼辦呢,那這個copy有什麼用呢是吧! child1.job.push('programmer'); console.log(father1); //{ name: 'shangdi',age: 1000,job: [ 'teacher', 'cook', 'programer' ] } console.log(child1);//{ name: 'shangdi',age: 1000,job: [ 'teacher', 'cook', 'programer' ] }

有上面可分析:淺拷貝的時候,當我們改變子物件的陣列的時候,父物件竟然也跟著改變,也就是說,子物件和父物件在淺拷貝的時候他們指向同一個記憶體的陣列:
在這裡插入圖片描述如果我們想讓子物件的拷貝和父物件沒有一點關聯,那麼我們就必須用到深度拷貝
四、什麼是深度拷貝?
深度拷貝就是把父物件拷貝到子物件上,而且兩者的記憶體和以後的操作都互不影響的拷貝!

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;
}

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

    function deepCopy(obj) {
        return JSON.parse(JSON.stringify(obj));
    }

方法三:

function deepCopy(obj){
	var newobj,obj;
	if(obj.constructor==Object){
		newobj = new obj.constructor();
	}else{
		newobj = new obj.constructor(obj.valueOf());//valueOf()方法返回
	}
	for(var key in obj){
		if(newobj[key]=='object'){
			newobj[key] = deepCopy(obj[key]);
		}else{
			newobj[key] = obj[key];
		}
	}
}
newobj.toString = obj.toString;
newobj.valueOf = obj.valueOf;
return newobj;

方法四:

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;
};

方法五:(JavaScript面向物件程式設計指南)

 	function deepCopy(p,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; 
 }

理解深拷貝的遞迴你就能理解全部的拷貝精髓了