在 第13天 有稍微提到物件(包括陣列、函式等)具有 傳參考(Pass by Reference) 特性,今天將對此做更深入的探討,並且介紹正確複製物件的方法,也就是 淺層複製(Shallow Copy)
和 深層複製(Deep Copy)
。
我們已經知道將一個物件賦值給另一個變數時,實際上是將參考的記憶體位置傳遞給了新的變數,而不是將物件本身複製一份。也就是說,如果你修改了其中一個變數所指向的物件,另一個變數也會受到影響。
let a = '小明';
let b = '小明';
// 判斷結果為 true ,因為 a 和 b 是字串(原始型別)為傳值特性,且它們具有相同的值
if(a === b) { }
let c = { name: '小明' };
let d = { name: '小明' };
// 判斷結果為 false ,因為 c 和 d 是物件(物件型別)為傳參考特性,而它們參考不同的記憶體位置
if(c === d) { }
另外要知道的事,使用 const 宣告一個新的物件時,雖然無法改變該物件的值(參考的記憶體位置),但仍能更改裡面的屬性。
const person = { name: '王小明' };
person = { name: '王鮭魚' }; // 報錯
const person = { name: '王小明' };
person.name = '王鮭魚'; // 不會報錯
物件裡面的屬性的值如果放純值,此時它們具備傳值特性,所以修改這些屬性的值,不會影響到其他原本相同的值。
const here = 'Taipei';
const x = { city: here };
const y = { city: here };
console.log(x.city); // 輸出 'Taipei'
console.log(y.city); // 輸出 'Taipei'
x.city = 'Tainan';
console.log(x.city); // 輸出 'Tainan'
console.log(y.city); // 輸出 'Taipei'
物件裡面的屬性的值也可以放物件、陣列、函式等等,當然此時它們當然也具備傳參考特性,所以修改這些屬性的值,所有參考此屬性的地方也都會受到影響。
const here = { city: 'Taipei' };
const x = { area: here };
const y = { area: here };
console.log(x.area); // 輸出 'Taipei'
console.log(y.area); // 輸出 'Taipei'
x.area.city = 'Tainan';
console.log(x.area); // 輸出 'Tainan'
console.log(y.area); // 輸出 'Tainan'
另外,物件裡面的屬性值也可以參考到該物件本身(雖然實際開發時很少這樣做)。例如以下範例。
const a = { x: 123 };
a.y = a;
// 判斷結果為 true,因為 a.y 和 a.y.y 其實都是參考跟 a 相同的記憶體位置
if(a === a.y) { }
if(a === a.y.y) { }
console.log(a.x); // 輸出 123
console.log(a.y.x); // 輸出 123
console.log(a.y.y.x); // 輸出 123
//
在上面例子中我們知道了傳參考特性帶來的影響。但如果要複製一份全新的物件該怎麼做呢?請繼續看下去。
淺層複製(Shallow Copy)
的方式僅複製物件的第一層屬性,如果物件的屬性值是物件,它們將保持作為參考的關係。以下是淺層複製的兩種方式。
...
)const obj1 = { name: '小明', age: 20 };
const obj2 = { ...obj1 };
obj2.age = 25;
console.log(obj1.age); // 輸出 20
console.log(obj2.age); // 輸出 25
Object.assign()
const obj1 = { name: '小明', age: 20 };
const obj2 = Object.assign({}, obj1);
obj2.age = 25;
console.log(obj1.age); // 輸出 20
console.log(obj2.age); // 輸出 25
深層複製(Deep Copy)
是將物件中的所有屬性值(不論純值或物件類型)都複製新的一份到新物件中,也就是說**原物件和新物件間不會有屬性具有相同的參考。**以下是透過 JSON.parse
和 JSON.stringify
來進行深層複製的方式。
const obj1 = { name: '小明家', area: { city: 'Tainan' } };
const obj2 = JSON.parse(JSON.stringify(obj1));
obj2.area.city = 'Taipei';
console.log(obj1.area.city); // 輸出: 'Tainan'
console.log(obj2.area.city); // 輸出: 'Taipei'
JavaScript 中的物件傳參考特性使得在處理物件時需要格外小心。了解 淺層複製
和 深層複製
的差別並知道何時、怎麼使用,是開發者必須要學會的課題。淺層複製適用於大部分情況,但如果需要完全獨立的物件副本,則需要使用深層複製。要注意的是大量使用深層複製可能導致效率問題(物件越多層時越明顯)。那麼今天就到這邊,我們明天見~