函數是一等公民
函數式編程的目的
函數式編程的目的是使用函數來抽象作用在數據之上的控制流和操作,從而在系統中消除副作用并減少對狀態的改變。
函數式編程的概念
- 聲明式編程
- 純函數
- 引用透明
- 不可變性
聲明式編程
函數式編程屬于聲明是編程范式:這種范式會描述一系列的操作,但并不會暴露它們是如何實現的或是數據流如何傳過它們。
聲明式是將程序的描述與求值分離開來。它關注如何用各種表達式來描述程序邏輯,而不一定要指明其控制流或狀態關系的變化。
純函數
純函數指沒有副作用的函數。相同的輸入有相同的輸出,就跟我們上學學的函數一樣,常常這些情況會產生副作用。
改變一個全局的變量、屬性或數據結構
改變一個函數參數的原始值
處理用戶輸入
拋出一個異常
屏幕打印或記錄日志
查詢 HTML 文檔,瀏覽器的 Cookie 或訪問數據庫
純函數的性質
- 僅取決于提供的輸入,而不依賴于任何在函數求值或調用間隔時可能變化的隱藏狀態和外部狀態。
- 不會造成超出作用域的變化,例如修改全局變量或引用傳遞的參數。
引用透明
引用透明是定義一個純函數較為正確的方法。純度在這個意義上表面一個函數的參數和返回值之間映射的純的關系。如果一個函數對于相同的輸入始終產生相同的結果,那么我們就說它是引用透明。
使用純函數的代碼絕不會更改或破壞全局狀態,有助于提高代碼的可測試性和可維護性
函數式編程采用聲明式的風格,易于推理,提高代碼的可讀性。
函數式編程將函數視為積木,通過一等高階函數來提高代碼的模塊化和可重用性。
可以利用響應式編程組合各個函數來降低事件驅動程序的復雜性(這點后面可能會單獨拿一篇來進行講解)。
curry(柯里化)
curry 的概念很簡單:只傳遞給函數一部分參數來調用它,讓它返回一個函數去處理剩下的參數。
組合函數
undercore實現
function aCompose(...args) {
let length = args.length
let count = length - 1
let result
return function f1 (...arg1) {
result = args[count].apply(this, arg1)
if (count <= 0) {
count = length - 1
return result
}
count--
return f1.call(null, result)
}
}
函數式實現
function compose(...args) {
return (result) => {
return args.reduceRight((result, fn) => {
return fn(result)
}, result)
}
}
注意:要傳給 compose 函數是有規范的,首先函數的執行是從最后一個參數開始執行,一直執行到第一個,而且對于傳給 compose 作為參數的函數也是有要求的,必須只有一個形參,而且函數的返回值是下一個函數的實參。