深入理解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;
}
理解深拷貝的遞迴你就能理解全部的拷貝精髓了