(Day12) 物件,淺拷貝/深拷貝

前言

實作很常會遇到要讓物件複製出來,個別使用的狀況,在 JavaScript 物件複製分成兩種:

  • 淺拷貝
  • 深拷貝

淺拷貝(shallow copy)

這邊先來看看淺拷貝的範例:

1
2
3
4
5
6
7
8
var school = {
name:'Taipei University',
classes: {
teacher: 'Alex',
student:['Kevin','Clara','Rose']
}
}
var newSchool = Object.assign({}, school)

這樣就是典型的淺拷貝,接者修改 newSchoolname 屬性看看:

1
2
3
4
5
6
7
8
9
10
var school = {
name:'Taipei University',
classes: {
teacher: 'Alex',
student:['Kevin','Clara','Rose']
}
}
var newSchool = Object.assign({}, school)
newSchool.name = 'Taoyuan University'
console.log(school.name , newSchool.name)// 'Taipei University' , 'Taoyuan University'

可以發現 newSchoolname 確實獨立被修改了,不過要注意的是,這種淺拷貝方法只會對物件第一層的屬性生效,在第二層之下的仍然是上篇文章介紹的傳參考特性,這邊來看看修改第二層的範例:

1
2
3
4
5
6
7
8
9
10
var school = {
name:'Taipei University',
classes: {
teacher: 'Alex',
student:['Kevin','Clara','Rose']
},
}
var newSchool = Object.assign({}, school)
newSchool.classes.teacher = 'Mary'
console.log(school.classes.teacher, newSchool.classes.teacher) // 'Mary', 'Mary'

可以發現第二層以下的屬性,兩個物件仍會按照傳參考特性一同被修改。

在原生 JS 中淺拷貝方法較多,其他還有:

  • 使用物件展開方法:
1
2
3
4
5
6
7
8
9
10
11
12
var school = {
name:'Taipei University',
classes: {
teacher: 'Alex',
student:['Kevin','Clara','Rose']
},
}

var newSchool = { ...school }
newSchool.classes.teacher = 'Mary'
newSchool.name = 'Taoyuan University'
console.log(school.name , school.classes.teacher) //Taipei University Mary
  • for … in 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var school = {
name:'Taipei University',
classes: {
teacher: 'Alex',
student:['Kevin','Clara','Rose']
},
}
var newSchool = {}
for(var key in school ){
newSchool[key] = school[key]
}
newSchool.classes.teacher = 'Mary'
newSchool.name = 'Taoyuan University'
console.log(school.name , school.classes.teacher) //Taipei University Mary

不過實做中這種傳參考特性,有時反而達不到需求,這個時候就會想問了,JavaScript 中有沒有什麼方法,能複製兩個指向完全不同的物件?

而深拷貝就是能將物件記憶體指向完全分開的方法。

深拷貝(deep copy)

和淺拷貝不同,能夠做到深拷貝的功能,在原生 JavaScript 中只有一種寫法,如範例:

1
2
3
4
5
6
7
8
9
10
var school = {
name:'Taipei University',
classes: {
teacher: 'Alex',
student:['Kevin','Clara','Rose']
},
}
var newSchool = JSON.parse(JSON.stringify(school ))
newSchool.classes.teacher = 'Mary'
console.log( school.classes.teacher, newSchool.classes.teacher) //Alex, Mary

看到 JSON.stringify() 其實就會明白,這個方法是先透過 JSON.stringify() 將物件轉成字串,再使用 JSON.parse() 將字串轉成物件,由於有先轉成字串因此兩個物件的記憶體是完全分開的。

參考資料

  • JavaScript 核心篇 (六角學院)