在js的語法中,像Number,String,Boolean這樣的基本類型,它們的傳值方式是按值傳遞的,而想對象{a: 10, b: 20},它們的傳值是引用傳值的
對於對象來說,在這裏就總結一下深拷貝和淺拷貝時遇到的問題。
基本類型的按值傳遞,比如:a = 10, b = a,系統會爲a和b 分配不同的內存空間,彼此之間相互不影響。
var a = 10;
var b = a;
b = 20;
console.log(a); //輸出結果爲10
console.log(b); //輸出結果爲20
但是對於對象來說,結果就截然相反。
淺拷貝:
var obj1 = {
a: 10,
b: {
a: 'yf',
b: 'bl'
},
c: ['Bob','Tom','nick'],
d: function(){
console.log('Hello World');
}
};
var obj2 = obj1;
obj2.a = 30;
console.log(obj1);
console.log(obj2);
console.log(obj1 === obj2);
輸出結果如下:
由上面的代碼塊我可以很清楚的看到,obj1、obj2引用了同一塊內存空間,雖然我們對我obj2 進行了賦值操作,相當於給整個內存空間進行了賦值,所以obj1、obj2都進行了修改,這個就是所謂的淺拷貝。
深拷貝:
1、使用JSON數據解析實現深拷貝。
// 使用JSON數據解析來實現深度拷貝(JSON不能夠識別Function類型)
var obj1 = {
a: 10,
b: {
a: 'yf',
b: 'bl'
},
c: ['Bob','Tom','nick'],
d: function(){
console.log('Hello World');
}
};
function deepClone(obj){
return JSON.parse(JSON.stringify(obj));
}
var e = deepClone(obj1);
e.a = 20;
console.log(e);
console.log(obj1);
// 通過類型檢測判斷JSON是不識別Function類型的
console.log(typeof e.d);
console.log(typeof obj1.d);
輸出的結果如下所示:
使用JSON數據解析的這種方式是比較容易理解的,我們使用JSON.stringify將其轉化爲JSON字符串,這個時候重新生成的字符串和原來的對象是沒有什麼關係的,也就是說爲字符串重新開闢了一個內存空間,然後我們使用JSON.parse將其轉化爲對象,此時在新舊對象上的操作是彼此獨立的,所以我們輸出的結果中a的值是不同的。
**缺點:**這種方法存在一個致命性的錯誤,不能夠識別對象中的Function類型,JSON.stringify()不能夠識別Function,會返回undefined,所以這個方法只能用於只有數據的對象中。這個方法會拋棄對象的constructor,深拷貝之後,不管對象的構造函數是什麼,都會將其變成Object。
爲了解決這個問題,我們採用遞歸的方法來對 對象中的屬性進行遍歷輸出。
2、遞歸拷貝
function deepClone(Obj1, Obj2) {
var obj = Obj2 || {};
for (var i in Obj1) {
var prop = Obj1[i];
// 避免相互引用對象導致死循環,如initalObj.a = initalObj的情況
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : {};
arguments.callee(prop, obj[i]);
} else {
obj[i] = prop;
}
}
return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21, c: function(){alert('hello world');}}};
deepClone(obj, str);
console.log(str.a);
採用遞歸的方法,我們成功的解決了Function帶來的困擾,也避免了在遍歷的時候因相互調用對象導致的情況。
用一張話來結束深拷貝和淺拷貝的區別:
淺拷貝就是新舊對象引用同一個內存空間,一個改變則全部改變,即:一變全變;深拷貝就是舊對象引用原來的空間,新對象則新建空間,自己控制自己的大小。