Javascript中資料型別&深淺拷貝二
資料型別:
Javascript中有5種基本資料型別(簡單資料型別),分別為:Undefined, Null, Boolean, Number和String; 同時還含有一種複雜資料型別,即物件(雖然js中一切皆為物件)
其中Undefined和Null的區別為:
Undefined: 其實是已申明,但是並未賦值的變數,輸出的結構就是undefined
Null:是一個不存在的物件的結果
例如:
var a;
console.log(a); // undefined
console.log(document.getElementById('node' )); // 因為沒有id為node的節點,故輸出為null
資料的儲存
* 簡單資料型別的儲存
簡單資料型別的值佔據了記憶體中固定的儲存空間,並被儲存在棧記憶體中;
當複製一個簡單資料型別的值時,會建立這個值的一個副本,該副本會另行開闢一塊棧記憶體空間,改變該副本的值,不會影響原始變數的值。
var a = 'name';
var b = a; // 將a的值賦給變數b
b = 'age'; // 改變b的值
console.log(a); // name
console.log(b); // age;
注: 簡單資料型別的變數,不能給其新增屬性
var str = 'javascript';
str.attr ='cat'; // 給基本資料型別新增屬性
console.log(str.attr); // undefined
* 複雜資料型別的儲存
複雜資料型別即引用資料型別,它的值是物件,儲存在堆記憶體中,包含引用資料型別的變數實際上包含的並不是物件本身,而是一個指向該物件的指標。
所以直接將一個複雜資料型別的變數,賦給另一個變數,實際上只是複製了該變數的指標,因此,兩個變數實際上都指向同一個物件
當修改其中一個變數的值時,原始的變數也會跟著改變。
var obj = {
name:'dby',
age: 20
};
var objCopy = obj; // 複製obj給objCopy
objCopy.name = 'mxd'; // 改變複製的變數的屬性
console.log(obj); // Object{name:'mxd',age:20}
console.log(objCopy); // Object{name:'mxd',age:20}
深淺拷貝
淺拷貝
其實直接賦值就算是一種淺拷貝,對於基本資料型別的簡單賦值,是在棧記憶體中另行開闢了一片儲存空間,對拷貝過來的值進行改變,不會改變原始資料的值。
但是複雜資料型別的值賦值,只是複製了指向該值的指標,當賦值後的值改變時,原始資料的值也會跟隨改變,在實際工程中,這是我們不希望的
var obj = {
name:'dby',
age: 20
};
var objCopy = obj; // 複製obj給objCopy
objCopy.name = 'mxd'; // 改變複製的變數的屬性
console.log(obj); // Object{name:'mxd',age:20}
console.log(objCopy); // Object{name:'mxd',age:20}
slice實現陣列的拷貝
const arr = [1,3,4,5,6];
const arr1 = arr.slice(arr);
arr1[1] = 22; // 修改arr1的某個元素
console.log(arr); // [1,3,4,5,6]
console.log(arr1); // [1,22,4,5,6]
note: 由上面可以看到我們可以使用陣列的原生方法進行陣列的拷貝,但這個拷貝是不是深拷貝呢?,進一步實驗一下:
const arr = [1,3,4,5,[1,3]];
const arr1 = arr.slice(arr);
arr1[4][0] = 222;
console.log(arr); // [1,3,4,5,[222,3]] 原始陣列被更改了
console.log(arr1); // [1,3,4,5,[222,3]]
由這個小實驗可知,slice只能實現一維陣列的深拷貝,不能實現多維陣列的深拷貝。
Object.assign()實現物件的拷貝
const obj = {
x: 1,
y: 2
};
const obj1 = Object.assign({}, obj);
console.log(obj1); // { x: 1, y: 2}
obj1.x = 222;
console.log(obj); // { x: 1, y: 2 }
console.log(obj1); // {x: 222, y: 2 } 實現了物件的深拷貝,沒有改變原物件
const obj2 = {
x: 1,
y: {
m: 2
}
};
const obj3 = Object.assign({}, obj2);
console.log(obj3); // { x: 1, y: { m: 2 } }
obj3.y.m = 222; // 修改obj3的屬性值
console.log(obj2); // { x: 1, y: { m: 222 } } // 將原物件也更改了
console.log(obj3); // { x: 1, y: { m: 222 } }
由此可見,Object.assign()方法也只能實現一維物件的深拷貝,不能實現多維物件的深拷貝
深拷貝
陣列
陣列的深拷貝,可是借用陣列的方法slice()和concat()來解決,有侷限性,只能實現一維陣列的深拷貝
slice
var arr = ['cat','dog','mouse'];
var arrCopy = arr.slice(0); // 返回arr中0-(length-1)長度的陣列,不會改變原陣列
arrCopy[0] = 'tigger'; // 改變arrCopy第一項為tigger
console.log(arr); // ['cat','dog','mouse']
console.log(arrCopy); // ['tigger','dog','mouse']
concat
var arr = ['cat','dog','mouse'];
var arrCopy = arr.concat(); // arrCopy = ['cat','dog','mouse'];
arrCopy[0] = 'tigger'; // 改變arrCopy第一項為tigger
console.log(arr); // ['cat','dog','mouse']
console.log(arrCopy); // ['tigger','dog','mouse']
物件
方法一:通過定義一個新的物件,並遍歷原物件,將屬性逐一拷貝上去。
var obj = {
name:'dby',
age:20
};
var objCopy = new Object();
objCopy.name = obj.name;
objCopy.age = obj.age;
objCopy.name = 'chris';
console.log(obj); // Object{name:'dby',age:20}
console.log(objCopy); // Object{name:'chris',age:20}
方法二:封裝一個方法來處理物件的深拷貝
var obj = {
name:'dby',
age:20
};
var deepCopy = function(source){ // 物件深拷貝函式
var result = {};
for(var key in source){
if(typeof source[key] === 'object'){
// 如果屬性為object型別,則遞迴呼叫深拷貝函式
result[key] = deepCopy(source[key]);
}else{
result[key] = source[key];
}
}
return result;
}
var objCopy = deepCopy(obj);
objCopy.name = 'chris';
console.log(obj); // Object{name:'dby',age:20}
console.log(objCopy); // Object{name:'chris',age:20}