JavaScript Copy




如果我想要複製物件的值,卻不希望在複製後,只要更動其值,原變數也會受到影響,該怎麼做呢?

本篇將介紹關於 JavaScript 中的「淺拷貝」以及「深拷貝」。


原始型別在賦值時一般是直接傳值的方式(pass by value):

1
2
3
4
5
6
let a= 1;
let b = a;
b = 2;

console.log(a); //1
console.log(b); //2

但是物件會是傳指標(pass by Reference):

1
2
3
4
5
6
let obj1 = { a:1, b:2, c:3};
let obj2 = obj1;
obj2.b = 4

console.log(obj1) //{ a:1, b:4, c:3}
console.log(obj2) //{ a:1, b:4, c:3}

淺拷貝 Shallow Copy

原本 ab 是指向同一塊記憶體位址;透過重新賦值的方式,讓 b 指向一個新的記憶體位址:

1
2
3
4
5
6
7
8
9
var a = {name: 'William'}

var b = a;

b = {name: "Teddy"};

console.log(a.name); // William

console.log(b.name); // Teddy

同樣的範例還有這個,我們直接把 a 取出的值放到屬性名稱後面:

1
2
3
4
5
6
7
8
9
10
var a = {name: 'William', age: 70};

var b = {name: a.name, age: a.age};

b.name = "Teddy";


console.log(a.name); // William

console.log(b.name); // Teddy

也可以使用 Object.assign 的方式來複製:

1
2
3
4
5
var a = {name: 'William', age: 70};
var b = Object.assign({}, a);
b.name = "Teddy";
console.log(a.name); // William
console.log(b.name); // Teddy

但是以上使用淺拷貝只能複製一層。

當拷貝到第二層的時候就會發生問題:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var obj = {

a: 1,

b: {

b1: 2,

b2: 3

}
};
var obj2 = Object.assign({}, obj);


obj2.a= 10;

obj2.b.b1= 20;


console.log(obj.a); // 1

console.log(obj.b.b1); // 20

物件 obj2 改變 a 並不會影響物件 obja ,但是改變再深一層的物件的值,就會影響到原本 ab.b1 了!

讓我們在看一個例子:

1
2
3
4
5
6
7
8
let obj1 = {a:{b:5}};
let obj2 = {a:obj1.a};//都把a指派到{b:5}這個物件的reference
obj2.a.b = 10

console.log(obj1) //{a:{b:10}}; 被指派到10
console.log(obj2) //{a:{b:10}};
console.log(obj1 === obj2) //false 實際上不是同物件    
console.log(obj1.a === obj2.a) //true 但是第二層物件實際上相同

當我們要確保物件是整個複製,而不是只複製 reference 時就需要用到 Deep Copy

深拷貝 Deep Copy

JSON.parse(JSON.stringify(object_array)):

  • JSON.parse():把字串轉成物件
  • JSON.stringify():把物件轉成字串。

我們可以把物件先轉換成 JSON 再 parse 回來

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj = {
    a: 1,
    b: {
        b1: 2,
        b2: 3
    }
};

var obj2 = JSON.parse(JSON.stringify(obj));

obj2.a= 10;
obj2.b.b1= 20;

console.log(obj.a); // 1
console.log(obj.b.b1); // 2

也可以透過使用第三方套件(ex: JQuery、lodash)的方式:

Lodash
1
2
3
4
5
6
7
8
9
var obj = {
    a: 1,
    b: {
        b1: 2,
        b2: 3
}
};

var obj2 = cloneDeep(obj);
Jquery

深拷貝和淺拷貝對應的參數是 true 或 false 。
預設情況是 false(淺拷貝),而且 false 不能夠寫出來,也就是你想要淺拷貝,那就什麼參數都不用帶。如果想帶入參數,只能傳入 true(深拷貝)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var obj = {};
var obj2 = {
    a: 1,
    b: {
        b1: 2,
        b2: 3
    }
};
// 深拷貝

$.extend(true, obj, obj2);

obj.b.b1= 20;

// b 並沒有因為 a 的改動也跟著改動
console.log(obj2.b.b1); // 2

// 至於沒有加上參數就是淺拷貝
$.extend(obj,obj2);

參考文獻

  1. https://ithelp.ithome.com.tw/articles/10200860

  2. https://dustinhsiao21.com/2018/01/07/javascript-shallow-copy-and-deep-copy/