1、淺拷貝:兩個對象經過拷貝後雖然具有相同的屬性,但是他們都指向同一個內存空間。操作會引起引用,同一地址的變量一起改變。
let a = {x: 1}
let b = a
b.x = 2
console.log(b) // {x: 2}
console.log(a) // {x: 2}
2、深拷貝:兩個對象除了拷貝了一樣的屬性, 沒有任何其他關聯(指向不同的內存空間)。
實現深拷貝的方法:
(1)ES5 for循環賦值
//普通對象,單層屬性
let souceObj = {x: 1, y: 2}
function clone(souceObj, targetObj = {}) {
for (const key in souceObj) {
targetObj[key] = souceObj[key]
}
return targetObj
}
let targetObj = clone(souceObj)
targetObj.x = 10
console.log(souceObj) //{ x: 1, y: 2 }
console.log(targetObj) //{ x: 10, y: 2 }
(2)JSON parse stringify
let souceObj = {x: 1, y: 2}
let targetObj = JSON.parse(JSON.stringify(souceObj))
targetObj.x = 10
console.log(souceObj) //{ x: 1, y: 2 }
console.log(targetObj) //{ x: 10, y: 2
這樣做是真正的Deep Copy,這種方法簡單易用。
但是這種方法也有不少壞處,譬如它會拋棄對象的constructor。也就是深拷貝之後,不管這個對象原來的構造函數是什麼,在深拷貝之後都會變成Object。
這種方法能正確處理的對象只有 Number, String, Boolean, Array, 扁平對象,即那些能夠被 json 直接表示的數據結構。RegExp對象是無法通過這種方式深拷貝。
也就是說,只有可以轉成JSON格式的對象纔可以這樣用,像function沒辦法轉成JSON。
(3)ES6 操作符 ...
let souceObj = {x: 1, y: 2}
let targetObj = {...souceObj}
targetObj.x = 10
console.log(souceObj) //{ x: 1, y: 2 }
console.log(targetObj) //{ x: 10, y: 2 }
(4)ES6 Object.assign()
let souceObj = {x: 1, y: 2}
let targetObj = Object.assign({}, souceObj)
targetObj.x = 10
console.log(souceObj) //{ x: 1, y: 2 }
console.log(targetObj) //{ x: 10, y: 2 }
(5)遞歸深拷貝
function deepCopy(obj) {
// 只拷貝對象
if (typeof obj !== 'object') return;
// 根據obj的類型判斷是新建一個數組還是一個對象
var newObj = obj instanceof Array ? [] : {};
for (let key in obj) {
// 遍歷obj,並且判斷是obj的屬性才拷貝
if (obj.hasOwnProperty(key)) {
// 判斷屬性值的類型,如果是對象遞歸調用深拷貝
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
}
}
return newObj;
}