函數式程式設計
假設我們要將下面一組數字進行排序:
很快地,我們可能就想出以下的程式碼來達成需求:
| function sortInput() { |
| |
| for (let i = 0; i < input.length; ++i) { |
| for (let j = 0; j < input.length - i - 1; ++j) { |
| if (input[j] > input[j + 1]) { |
| [input[j], input[j + 1]] = [input[j + 1], input[j]] |
| } |
| } |
| } |
| } |
| var input = [3, 2, 4, 1] |
| sortInput() |
| console.log(input) |
很快地,你可能會發現這個方法沒辦法重複使用,因為你沒辦法排序其他數列:
| function sort(input) { |
| |
| for (let i = 0; i < input.length; ++i) { |
| for (let j = 0; j < input.length - i - 1; ++j) { |
| if (input[j] > input[j + 1]) { |
| [input[j], input[j + 1]] = [input[j + 1], input[j]] |
| } |
| } |
| } |
| |
| return input |
| } |
| var input = [3, 2, 4, 1] |
| console.log(sort(input)) |
| console.log(sort([5, 8, 7, 6])) |
假設,此時有一個排序,它是要降冪排序:
| function sortBy(comparator, input) { |
| |
| for (let i = 0; i < input.length; ++i) { |
| for (let j = 0; j < input.length - i - 1; ++j) { |
| if (input[j] > input[j + 1]) { |
| [input[j], input[j + 1]] = [input[j + 1], input[j]] |
| } |
| } |
| } |
| |
| return input |
| } |
此時,得到一個意外的答案,奇數的陣列竟然是空的:
| var input = [1, 2, 3, 4] |
| console.log(getEvenNumbers2(input)) |
| console.log(getOddNumbers(input)) |
原因是 Array.prototype.splice
這類的函式帶有「副作用」,它會更改原始陣列,讓我們優化一下:
| function getEvenNumbers3(input) { |
| |
| input = input.slice() |
| |
| for (let i = input.length - 1; i >= 0; --i) |
| if (input[i] % 2 === 1) |
| input.splice(i, 1) |
| |
| return input |
| } |
| |
| function getOddNumbers2(input) { |
| |
| input = input.slice() |
| |
| for (let i = input.length - 1; i >= 0; --i) |
| if (input[i] % 2 === 0) |
| input.splice(i, 1) |
| |
| return input |
| } |
接著測試看看:
| var input = [1, 2, 3, 4] |
| console.log(getEvenNumbers3(input)) |
| console.log(getOddNumbers2(input)) |
一切正常,但你可能會問 getEvenNumbers3
好像與 getOddNumbers2
看起來差不多?
| function filter(predicate, input) { |
| |
| input = input.slice() |
| |
| for (let i = input.length - 1; i >= 0; --i) |
| if (predicate(input[i]) === false) |
| input.splice(i, 1) |
| |
| return input |
| } |
抽取共有的邏輯之後,我們可以得到一個 filter
「高階函式」,接受一個斷言函式,用以判斷是否要保留各個元素:
| var input = [1, 2, 3, 4] |
| |
| const isOdd = n => n % 2 === 1 |
| const isEven = n => n % 2 === 0 |
| |
| console.log(filter(isOdd, input)) |
| console.log(filter(isEven, input)) |
我們可以透過日常將這些函式抽取出來,來提升開發速度,也可以直接使用社群維護的函式庫,例如「Ramda」:
| import * as R from 'ramda' |
| |
| var input = [1, 2, 3, 4] |
| |
| const isOdd = n => n % 2 === 1 |
| const isEven = n => n % 2 === 0 |
| |
| console.log(R.filter(isOdd, input)) |
| console.log(R.filter(isEven, input)) |
結論
- 提升開發速度 🚀
- 代碼可讀性高
- 降低專案體積 📦
- 避免邊夜問題
- 容易除錯追蹤
- 提升可測試性
- …族繁不及備載