今天不做作品,而是討論一下在 JavaScript 中的複製。或許你有遇過下面問題
let arr1 = [1, 2, 3];
let arr2 = arr1;
arr2[2] = 5;
arr2; // [1, 2, 5]
arr1; // [1, 2, 5]
原本的 arr1
居然也被更改了!假如你也正有著個疑惑那就接著看下去吧!如果你已經知道原因那也看下去吧!
先看範例
let a = 1;
let b = a;
b = 3;
b; // 3
a; // 1
當 a
是基本型別並 b = a
時,a
實際上複製一份自己的值給 b
這個變數,也因此當更改 b
的值的時候 a
的值不會一起被更改,而這種特性適用所有基本型別。
假設現在有一 array
let fruits = ['watermelon', 'papaya', 'banana', 'lemon'];
接著用新的變數複製一份 fruits
的值並更改內容
let fruits2 = fruits;
fruits[0] = 'pamelo';
fruits2; // ['pamelo', 'papaya', 'banana', 'lemon']
fruits; // ['pamelo', 'papaya', 'banana', 'lemon']
原本的 fruits
竟然也被改到了!
這是因為當對 array 使用 fruits2 = fruits
時,不同於基本型別複製了「值」,實際上是複製了一份指向 ['watermelon', 'papaya', 'banana', 'lemon']
的參考點(reference),或者可以說現在 fruits
以及 fruits2
都指向相同的 array ['watermelon', 'papaya', 'banana', 'lemon']
,所以當更改其中一個時,另一個也會被改變。
既然 fruits2 = fruits
不會複製實際值,那我們要如何複製 array 的值呢?
請參考使用下列幾種方法,如果有其他方法也歡迎補充~
這個方法之前在將 array-like 轉成 array 時常用到,但它也可以用來複製 array 喔!
let fruits3 = Array.from(fruits);
fruits3[0] = 'pamelo';
fruits3; // ['pamelo', 'papaya', 'banana', 'lemon']
fruits; // ['watermelon', 'papaya', 'banana', 'lemon']
原本的 fruits
沒有被改到,很好!
slice()
也可以達成一樣的效果,它原本的使用目的是取出部分 array 內容,但同樣能用於複製
let fruits4 = fruits.slice(); // 若沒有帶入參數則會是整個 array
fruits4[0] = 'pamelo';
fruits4; // ['pamelo', 'papaya', 'banana', 'lemon']
fruits; // ['watermelon', 'papaya', 'banana', 'lemon']
第三種方法是使用 ES6 的新語法展開運算子(Spread Operator)
let fruits5 = [...fruits]; // 若沒有帶入參數則會是整個 array
fruits5[0] = 'pamelo';
fruits5; // ['pamelo', 'papaya', 'banana', 'lemon']
fruits; // ['watermelon', 'papaya', 'banana', 'lemon']
上面提到的特性(複製參考點而不是實際的值)不只適用 array,同樣也適用於 object。
舉例來說
let person = {
name: 'Henry',
age: 66
}
let man1 = person;
man1.age = 30;
man1; // {name: 'Henry', age: 30}
person; // {name: 'Henry', age: 30}
man1
同樣只複製到 {name: 'Henry', age: 66}
的參考點而不是實際值,以下列出 object 的複製方法
let man2 = Object.assign({}, person, {age: 87, job: 'teacher'});
man2; // {name: 'Henry', age: 87, job: 'teacher'}
person; // {name: 'Henry', age: 66}
原本的 object 沒有被動到,耶!
目前我只有想到這個複製方法,如有其他方法歡迎提供
上述的方法雖然可以複製,但其實只是淺層複製(只複製一層),也就是當 object/array 中的項目有 object/array 時會失效,見下方範例
let cat = {
name: 'Miao',
birthYear: 2006,
description: {
friendly: false,
fat: true
}
}
let cat2 = Array.from(cat);
cat2.birthYear = 2012;
cat2.description.fat = false;
cat2; // {name: 'Miao', birthYear: 2012, description: {friendly: false, fat: false}}
cat; // {name: 'Miao', birthYear: 2006, description: {friendly: false, fat: false}}
可以看到 cat.description.fat
還是被改了
如果想要完整複製這種 object/array,可以考慮寫一個 function 並使用迴圈來複製每一層
array 和 object 除了上述的幾種方法其實還有一種方法,有用但很糙,請將它視為沒有辦法的辦法
let cat3 = JSON.parse(JSON.stringify(cat));
如果試著更改 description
中的內容,會發現原本的 cat
沒有被改到,這是因為 JSON.stringify
先將 object 轉成了 string(此時原本的參考點就不復存在了)並透過 JSON.parse
還原
希望這篇提到的內容對於你了解複製值&參考點有幫助,沒有的話也沒關係。