javaScript中的深拷貝與淺拷貝

剛學 js 的時候就曾經寫過這樣的程式碼,那時對物件的參考指向還沒有很熟悉,所以就遇到了一個問題…

1
2
3
4
var a = { name: "tom", age: 30, gender: "male" };
var b = a;
b.name = "sara";
console.log(a.name); // sara

恩?為什麼我修改了 b,a 物件也會跟著變動呢?想必大家都知道物件型別是傳址(reference) ,所以 b 參考指向為 a,如同影分身一般,為了不改變原本的物件,我們就必須使用淺拷貝和深拷貝來避免這個問題。

那麼,就稍微介紹一下深拷貝與淺拷貝的差異

  • 淺拷貝:僅能複製淺層(第一層),有較深的資料結構時,還是會參考到原本的物件,因此就會改動到原本的物件。
  • 深拷貝:能夠建立一模一樣的物件,兩個為獨立個體,不會共用同個記憶體,個別修改並不會交互影響。

淺拷貝有以下的方式

  • 展開運算子
1
2
var a = { name: "tom", age: 30, gender: male };
var b = { ...a };
  • object assign

只處理深度為一層的物件沒問題

1
2
var a = { name: "tom", age: 30, gender: male };
var b = Object.assign({}, a);

但是深度一層以上還是會修改到原本的物件

1
2
3
4
5
6
7
8
9
10
var a = {
name: "tom",
age: 30,
gender: "male",
birthday: { month: "July", date: 15 },
};
var b = Object.assign({}, a);

b.birthday.date = 8;
console.log(a.birthday.date);

深拷貝

  • JSON.parse( JSON.stringify( object ) )

但是如果物件裡面有包含 function 就會失效,因為 JSON.stringify 會將無法轉換為文字的屬性忽略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var a = {
name: "tom",
age: 30,
gender: "male",
action: function () {
console.log("sayHi");
},

companion: undefined,
fortune: NaN,
};
var b = JSON.parse(JSON.stringify(a));

//可以看到 function、undefined 都消失了,且 NaN 會被轉換成 null

console.log(b); //{name: "tom", age: 30, gender: "male",fortune: nul}

loadsh 套件

1
2
3
import _ from "loadsh";
var a = { name: "tom", age: 30, gender: "male" };
var b = _.cloneDeep(a);

自製的遞回 function!!

1
2
3
4
5
6
7
8
9
10
11
12
13
const deepCopy = (val) => {
let newVal = Array.isArray(val) ? [] : {};
for (let v in val) {
if (val.hasOwnProperty(v)) {
if ('object' === typeof val[v]) {
newVal[v] = deepCopy(val[v]);
} else {
newVal[v] = val[v];
};
}
};
return newVal;