先來看下基礎的資料型別和淺拷貝。
JavaScript 資料型別
基本資料型別
- String
- Number
- Null
- Undefined
- Boolean
- Symbol(ES6 新增)
基本資料型別存放在棧記憶體中,可直接訪問和修改變數的值,基本資料型別不存在拷貝。
引用型別
- Object
引用型別值為物件,存放在堆記憶體中。
在棧記憶體中變數儲存的是一個指標,指向對應在堆記憶體中的地址。當訪問引用型別的時候,要先從棧中取出該物件的地址指標,然後再從堆記憶體中取得所需的資料。
淺拷貝
基礎的引用拷貝
var source=[1,2,3]
var target=source
source[0]=0
console.log(source,target) // [ 0, 2, 3 ] [ 0, 2, 3 ]
複製程式碼
因為 source 陣列是 Object 型別,是引用型別,target 相當於多了一個指標,指標指向的值變了,所以兩個陣列都改變。
concat、slice、Object.assign()
var source=[1,2,3]
var target=source.concat()
source[0]=0
console.log(source,target) // [ 0, 2, 3 ] [ 1, 2, 3 ]
複製程式碼
這個像深拷貝嗎?是深拷貝嗎?
看下面這個輸出:
var source=[1,2,[3,4],{name:'linmu'}]
var target=source.concat()
source[3].name='beiyang'
console.log(source[3],target[3]) // {name:'beiyang'} {name:'beiyang'}
複製程式碼
當陣列 source 包含物件時,slice 和 concat 拷貝出來的陣列中的物件還是共享同一個記憶體地址,所以仍是淺拷貝。
深拷貝
深拷貝是可以完美的解決淺拷貝的弊端,重新開闢一塊地址,兩者互不關聯。
普通深拷貝
JSON 物件是 ES5 中引入的新的型別(支援的瀏覽器為 IE8+),JSON 物件 parse 方法可以將 JSON 字串反序列化成 JS 物件,stringify 方法可以將 JS 物件序列化成 JSON 字串,藉助這兩個方法,可以實現簡單物件的深拷貝,但不能處理函式、正則等物件。
JSON.parse(JSON.stringify())
複製程式碼
最強深拷貝
可解決上面不能處理函式,setter,getter 等問題,方便在面試時手寫:
function cloneObject (target, source) {
//獲取物件下的所有屬性,包括不可遍歷屬性
var names = Object.getOwnPropertyNames(source);
for (var i = 0; i < names.length; i++) {
// 獲取每個屬性的描述資訊
var desc = Object.getOwnPropertyDescriptor(source, names[i]);
if (typeof (desc.value) === "object" && desc.value !== null) {
var obj;
if (Array.isArray(desc.value)) {
obj = [];
} else {
obj = {};
}
// 如果 desc 的值是物件的話,遞迴呼叫,直至屬性為費物件
Object.defineProperty(target, names[i], {
configurable: desc.configurable,
enumerable: desc.enumerable,
value: obj,
writable: desc.writable
});
cloneObject(obj, desc.value);
} else {
// 如果 desc 的值不是物件,直接宣告
Object.defineProperty(target, names[i], desc);
}
}
return target;
}
複製程式碼