js的變量分為簡單數據類型和復雜數據類型(即引用類型)。
簡單數據類型在內存中占據着固定大小的空間,被保存在棧內存中,在簡單數據類型中,當一個變量指向另一個變量時,只是創建了值的副本,兩個變量只是占用的空間大小相同,值相同,但是存儲的位置不同。因此,當其中一個值改變的時候,不會對另一個值有影響。
對於引用類型,值是對象,保存在堆內存中。當一個變量指向另一個變量時,它們其實指向的是同一個內存空間,變量保存的是指向實際對象的指針。從一個變量向另一個變量復制引用類型的值,復制的其實是指針地址而已,因此兩個變量最終都指向同一個對象。
引用類型:
var obj1={name:'sally'};
var obj2=obj1;
obj2.age=9;
console.log(obj1)
//{name: "sally", age: 9}
console.log(obj2)
{name: "sally", age: 9}
obj賦值給obj2后,改變其中一個對象的屬性值,兩個對象都發生了改變,根本原因就是obj和obj2兩個變量都指向同一個指針,賦值時只是復制了指針地址,它們指向同一個引用。
1、淺拷貝
以上就是簡單的淺拷貝的例子,但是改變任一變量都會互相影響。
2、深拷貝
深拷貝就是完全復制變量的一份給其他的變量備份。解決淺拷貝帶來的問題;
1、數組
(1)只包含簡單數據類型的數組
對於僅僅包含簡單數據類型的數組來說可以使用slice和concat來實現;
如:
var a = b.slice(0);
var b = [].concat(b);//或者b.concat();
- arrayObj.slice(start, [end]) 該方法返回一個 Array 對象,其中包含了 arrayObj 的指定部分。不會改變原數組
- arrayObj.concat() 方法用於連接兩個或多個數組。該方法不會改變現有的數組,而僅僅會返回被連接數組的一個副本。
(2)數組元素中存在數組(復雜類型)
如:
var a=[1,3,4,[46,23]];
var b = a.slice(0);
b[3][0]='34';
console.log(a,b)
//a、b均為[1,3,4,[34,23]
可見,當改變b[3].name的時候,a也會被改變。解決次問題可以定義以下函數:
function deepCopyArray(arr){
var output = arr.slice(0);
for(var index=0;index<output.length;index++){
if(Object.prototype.toString.call(output[index])==='[object Array]'){
output[index]=copyArray(output[index]);
}
}
return output;
}
2、對象
對象的深拷貝實現原理: 定義一個新的對象,遍歷源對象的屬性 並 賦給新對象的屬性
如:
var a={name:'sally',age:26};
var b={};
b.name = a.name;
b.age = a.age;
b.hobby = 'sing'
console.log(a); //{name: "sally", age: 26}
console.log(b); //{name: "sally", age: 26, hobby: "sing"}
如果對象中屬性值僅僅是簡單數據類型,遍歷源對象的屬性,將對應的屬性值賦給最后的對象即可,如果源對象中的屬性值是對象的話就需要通過遞歸來實現了。
如下實現的對象深拷貝函數:
function deepCopyObject(source){
var result = {};
for(var key in source) {
if(typeof source[key] === 'object') {
result[key] = deepCopy(source[key]) //如果屬性值為對象,遞歸
} else {
result[key] = source[key]
}
}
return result;
}
3、數組對象混合的通用深拷貝函數
function deepCopy(input){
var output;
if(Object.prototype.toString.call(input)==='[object Array]'){
output = input.slice(0);
}
if(Object.prototype.toString.call(input)==='[object Object]'){
output = {};
for(let key in input) {
output[key] = input[key]
}
}
for(let key in output){
if(typeof output[key]==='object'){
output[key]=deep(output[key]);
}
}
return output;
}
驗證代碼
(1)數組包含數組元素
var a=[1,3,4,[46,23]];
var b = deep(a);
b[3][0]=34;
console.log(a,b)
(2)數組包含數組和對象
var a=[1,3,4,[46,23],{name:'sally'}];
var b = deep(a);
b[3][0]=34;
b[4].name='bob';
console.log(a,b)
(3)對象包含對象
var a={name:'sally',list:{num:[1,2,3,4]}};
var b = deep(a);
b.list.num[2]=34;
b.name='bob';
console.log(a,b)
(4)對象包含數組
var a={name:'sally',list:[1,2,3,4]};
var b = deep(a);
b.list[0]=34;
b.name='bob';
console.log(a,b)