學了這麼長時間的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;
}