FP 三部曲 (Declarative、Pure Function 與 Immutable) 中的第二部曲就是 Pure Function,但也由於 ECMAScript 對於 Argument 有 Pass by Value 與 Pass by Reference 兩種,且型別並沒有完整支援 Immutable,因此實現 Pure Function 時有些 Tricky。
Version
macOS Catalina 10.15
VS Code 1.38.1
Quokka 1.0.256
ECMAScript 2015
Primitive
let data = 1;
let fn = x => x + 1;
fn(data); // ?
data; // ?
若 argument 為 primivite,因為 primitive 是以 pass by value 方式傳遞 argument,且 primitive 本身也是 immutable,因此實現 pure function 很直覺容易。
Array
let data = [1, 2, 3];
let fn = val => arr => {
arr.push(val);
return arr;
}
fn(4)(data); // ?
data; // ?
若 argument 為 array 則有些 tricky,因為 ECMAScript 是以 pass by reference 傳遞 array,由於 Array.prototype.push()
是以 side effect 直接更改原本 array,而非 immutable,又因 pass by reference 間接修改既有資料,因此不是 pure function。
let data = [1, 2, 3];
let fn = val => arr => [...arr, val];
fn(4)(data); // ?
data; // ?
若要實現真正 pure function,則要改用 ES6 的 spread operator 展開,如此僅管 arr
argument 是 pass by reference,但因為 spread operator 沒有 side effect 且 immutable,所以不會更改原本 array,也因此不會修改既有資料,因此為 pure function。
Object
let data = {
name: 'Sam'
};
let fn = k => v => obj => {
obj[k] = v;
return obj;
}
fn('age')(18)(data); // ?
data; // ?
若 argument 為 object 也有些 tricky,因為 ECMAScript 是以 pass by reference 傳遞 object,由於 []
是以 side effect 直接更改原本 object,而非 immutable, 又因 pass by reference 間接修改既有資料,因此不是 pure function。
let data = {
name: 'Sam'
};
let fn = k => v => obj => ({...obj, ...{k: v}})
fn('age')(18)(data); // ?
data; // ?
若要實現真正 pure function,則要改用 ES6 的 spread operator 展開,如此僅管 obj
argument 是 pass by reference,但因為 spread operator 沒有 side effect 且 immutable,所以不會更改原本 object,也因此不會修改既有資料,因此為 pure function。
Conclusion
- 由於 ECMAScript 對於 primitive 與 array、object 有兩種完全不同處理方式,因此在實現 pure function 時不太一樣
- ES5 早期有不少 side effect 寫法,ES6 則提供了 spread operator 避免 side effect