前言
近來瀏覽一篇關(guān)于JavaScript的數(shù)據(jù)引用類型的淺拷貝和深拷貝的文章,感覺作者講的淺顯易懂,解決我一直以來不太理解深拷貝和淺拷貝的問題,所以要記錄下來,以免以后忘記。
文前案例
let num1 = 1, num2 = num1
num2 = 2
console.log(num1) // 1
console.log(num2) // 2
let obj1 = {name: 'Tom', age: 19}, obj2 = obj1
obj2.age = 23
console.log(obj1 ) // {name: 'Tom', age: 23}
console.log(obj12) // {name: 'Tom', age: 23}
看到這個案例,可能會有點懵,這和拷貝有什么關(guān)系?且聽我說來,從第一個例子看來,當(dāng)改變num2時,num1并沒有改變,按照常規(guī)想法,obj1.age應(yīng)該不會隨著obj2.age的改變而改變的,但實際并非如此,這是因為數(shù)據(jù)類型的差別,在JavaScript中數(shù)據(jù)主要分為兩種類型,基本類型和引用類型,基本類型主要有Number,String,Null,Undefined,Boolean等,引用類型Array,Object等,基本類型在內(nèi)存中的儲存和引用類型是不一樣的,引用類型的變量名實際是指向一個內(nèi)存地址名,非實際的內(nèi)存,所以當(dāng)引用類型的copy賦值時,實際賦值的是內(nèi)存地址名。而此時的obj1和obj2指向的是同一個內(nèi)存地址,當(dāng)修改其中一個時,會修改內(nèi)存地址中的數(shù)據(jù),從而導(dǎo)致另一個改變。這就是淺拷貝,只是拷貝了引用的地址名,所以深拷貝與淺拷貝的概念只是存在于引用類型中。
深拷貝與淺拷貝
看到的那篇文章用了一個很形象的日常例子來說明深拷貝和淺拷貝的區(qū)別,連鎖店和普通店,深拷貝就像連鎖店,每個店都是深度拷貝,其中一個店貨物改變,并不影響其他店,而淺拷貝就像普通店的鑰匙,可以分給不同店長,他們改變貨物,就會影響整個店的貨物。
淺拷貝其實不是我們要研究的,我們往往是希望深拷貝,因為在實際應(yīng)用中,我們是不希望在修改拷貝的時候,影響父本的數(shù)據(jù),現(xiàn)在我們只說說Array和Object怎么樣深拷貝。
Array的深拷貝
在JS里內(nèi)置了很多操作數(shù)組的方法,那么有沒有可以深拷貝的方法呢?是有的。
let arr = [1,2,3]
let arr1 = arr.slice()
arr1[0] = 4
console.log(arr) // [1,2,3]
console.log(arr1) // [4,2,3]
let arr2 = [1,2,3,[4,5]]
let arr3 = arr2.slice()
arr3[3][0] = 6
console.log(arr2) // [1,2,3,[6,5]]
console.log(arr3) // [1,2,3,[6,5]]
從例子上我們可以看出,slice()方法的確可以深度拷貝,但只適用于一維數(shù)組的深拷貝,類似的concat()方法也是如此,那么如何實現(xiàn)多維數(shù)組的深拷貝呢?遞歸是一個好方法,且看我寫來。
function deepArray(arr) {
let result = []
let temp = null
arr.forEach((val, index) => {
temp = val
if (temp && temp instanceof Array) {
result[index] = deepArray(temp)
} else {
result[index] = temp
}
})
return result
}
let arr4 = [1,2,3,[4,5]]
let arr5 = deepArray(arr4)
arr5[3][0] = 6
console.log(arr2) // [1,2,3,[4,5]]
console.log(arr3) // [1,2,3,[6,5]]
object的深拷貝
既然在array里有深拷貝的方法,那么在object里也是有的,在es6的標(biāo)準(zhǔn)下,新加入的方法,Object.assign(),這是合并對象的一個方法,但是它也只是能夠深拷貝一維的對象,這里我們依然可以使用遞歸的方法完成對象的多維深拷貝
function deepCopy(obj) {
let result = {}
let keys = Object.keys(obj)
let temp = null
let key = null
for (let i = 0; i < keys.length; i++) {
key = keys[i]
temp = obj[key]
if (temp && typeof temp === 'object') {
result[key] = deepCopy(temp)
} else {
result[key] = temp
}
}
return result
}
var obj1 = {
x: {
m: 1
},
name: 'tom'
}
var obj2 = deepCopy(obj1)
obj2.x.m = 2
console.log(obj1) // {x: {m: 1}, name: 'tom'}
console.log(obj2) // {x: {m: 2}, name: 'tom'}