iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 8
1
Modern Web

ngrx/store 4 學習筆記系列 第 8

[ngrx/store-8] Javascript Mutable 跟 Immutable 資料型態

Javascript 的 Mutable, Immutable 資料型態

之前談到純函數的第二個要件是不能對可改變(Mutable) 資料做變更,那在 Javascript 中有哪些資料型態屬於 Mutable 資料型態呢?我們先來看 Javascript 的資料型態

Javascript 的資料型態

Javascript 總共有七個資料型態

  • 原始資料型態 (Primitive)
    • Boolean
    • Null
    • Undefined
    • Number
    • String
    • Symbol (new from ES6)
  • Object

所有的原始資料型態都是不可改變的(Immutable)

到底 Mutable 跟 Immutable 有什麼區別?先來看 Immutable 的例子

// for number
let a = 3;
let b = a;
b = b + 4;
console.log('b = ', b);   //  7
console.log('a = ', a);   //  3

// for string
let c = 'This is original string';
let d = c;
d = 'Now change the string';
console.log('d = ', d);   // Now change the string
console.log('c = ', c);   // This is original string

codepen
當其他變數(例如 b, d) 參照到原來變數 (a, c) 時,系統會在 Memory 指派一個新的位址,不會影響到原來的變數,那 Mutable 呢?

// for object
let e = { name: 'John', age: 40, title: 'Engineer'};
let f = e;
f.title = 'Manager';
console.log('f = ', f); // { name: 'John', age: 40, title: 'Manager' }
console.log('e = ', e); // { name: 'John', age: 40, title: 'Manager' }

可以發現新的變數(f)還是指到原來變數(e)的位址,所以當f改變時,e也跟著改變了,這是為了系統的優化而作的。然而不小心的話,可能會造成程式的臭蟲(bug), 而很難去追蹤,想像同一個物件共用在不同的地方(在 Angular 不同的 Component 或 module 中),當一個地方改變這個物件時,其他地方如果不注意,沒有隨之更改(比如說狀態),就會造成使用者的困擾,而這有時候很難去除錯。

Javascript 的物件是 Mutable

Javascript 的物件有包含了很多類型,幾個常見的

  • Date
  • Indexed Collection: 陣列(Array) 跟 typed Array
  • Keyed Collection: Map, Set...
  • Structure Data: JSON
  • ...

而這些在我們處理純函數時,都要特別留意小心。

純函數怎麼處理 Mutable

讓我們來看如何處理先前物件的例子

// immutable
let g = { name: 'Mary', age: 38, title: 'Engineer'};
let h = Object.assign({}, g, { title: 'Manager'});
console.log('h = ', f); // { name: 'Mary', age: 38, title: 'Manager' }
console.log('g = ', g); // { name: 'Mary', age: 38, title: 'Engineer'}

使用Object.assign() 來產生一個新的物件,它的語法是Object.assign(target, ...sources),我們的 target 是一個新的空的物件 {}, sources 是 g{title: 'Manager'},將 sources 結合後放入 target 中。這樣產生出來的變數h就跟原來的g脫勾了。
這個做法相當重要,往後我們在 Reducer 中,經常會見到這樣的語法。

陣列的處理

Javascript 的陣列是特殊的物件,當然也是 Mutable,比較特殊的是陣列有些 Operators 是 immutable的,例如 .map .filter .concat...,這些 Operators 會產生新的陣列,不會影響舊的陣列,我們可以直接用在純函數中。

然而有一些 Operator 是 mutable 的,例如 .push .pop .sort...,這些會直接改變原本的陣列,我們在純函數中就不該使用。

但是如果我們要加一個元素到陣列時,該怎麼做?看以下例子

// for array
let i = [1, 2, 3, 4];
let j = i;
j.push(5);
console.log('j = ', j);   // [1, 2, 3, 4, 5]
console.log('i = ', i);   // [1, 2, 3, 4, 5]

// immutable array
let k = [1, 2, 3, 4]
let l = [...k, 5];
console.log('l = ', l);  // [1, 2, 3, 4, 5]
console.log('k = ', k);  // [1, 2, 3, 4]

codepen

如果直接用 .push會影響原來陣列,這就不能用在純函數,而使用擴展語法 spread syntax,會產生新的陣列,不會對原本陣列產生影響。

擴展語法是一個相當強大的語法,有提議將這種語法擴充到物件上,雖然目前瀏覽器的支援還不夠,但因為我們使用 Angular typescript,它會幫我們做轉換,所以還是可以安心使用,舉個例子,之前的 let h = Object.assign({}, g, { title: 'Manager'}); 可以寫成 let h = {...g, title: 'Manager'}

現在,我們對於純函數有了一些工具來建置,接下來我們來深入探討一下 ngrx/store 如何實作 Flux 的概念。


上一篇
[ngrx/store-7] 純函數 (Pure Function)
下一篇
[ngrx-store-9] 用 Observable 來理解 Dispatcher 跟 Store
系列文
ngrx/store 4 學習筆記30

1 則留言

0
mattchen0512
iT邦新手 5 級 ‧ 2020-08-18 09:56:51

筆誤
// immutable
let g = { name: 'Mary', age: 38, title: 'Engineer'};
let h = Object.assign({}, g, { title: 'Manager'});
console.log('h = ', f); // { name: 'Mary', age: 38, title: 'Manager' }
這邊帶入的應該是 h
console.log('g = ', g); // { name: 'Mary', age: 38, title: 'Engineer'}

我要留言

立即登入留言