函數式編程是一種編程範式,也就是如何編寫程序的方法論,其它的編程範式還有面向對象編程,過程化編程等。函數式編程並非一個新興的概念,而是一個存在久遠的概念,隨着React的興起,函數式編程的概念又火了起來,Rxjs、lodash等庫都使用了函數式編程的思想。javascript
這個系列會從什麼是函數式編程提及,討論函數式編程的優缺點,如何使用函數式編程編寫優雅的代碼,最後會探討函數式編程在Javascript中的一些最佳實踐。java
解釋什麼是函數式編程並非個容易的事情,我也是在嘗試着學習和理解。函數式編程不是用function來編程,而是一種如何使用function,才能使它具有函數式的一種方法。要了解怎麼使用函數,首先咱們須要瞭解什麼是函數。編程
這裏簡單回顧一下初中的數學知識,當咱們看待一個函數時,它可能長這樣 y = f(x)
f(x) = 2x2 + 3
能夠看出函數的基本特色在於它有一個或多個輸入值,並進行必定的計算,而且獲得一個返回值。函數式編程中有一個術語叫作態射,來描述輸入值和輸出值的關係。緩存
這裏簡單討論下函數和程序的區別,這也是函數式編程的一大特性。程序是一系列指令的集合,它可能有輸入值也可能沒有,它可能有輸出值也可能沒有。而函數是接收輸入值並經過計算返回輸出值。bash
函數式編程的一大特色就是咱們應該儘可能的使用函數而非程序,固然在實際的開發中咱們不可避免的要進行I/O操做,可是若是你的計劃是使用函數式編程,那麼在開發中就應該儘量多的使用函數,而不是程序。閉包
這是函數式編程的一個重要概念,函數與其餘數據類型同樣,處於平等地位,能夠賦值給其餘變量,也能夠做爲參數,傳入另外一個函數,或者做爲別的函數的返回值。併發
Javascript自己不是純的函數式編程語言,能夠說是一個多範式的編程語言,可是因爲其函數是一等公民特性,咱們能夠把函數做爲參數進行傳遞,也能夠將函數做爲函數的返回值等特性,使得Javascript能夠進行函數式編程。app
同時這個特性是函數式編程的一些核心內容,好比閉包、高階函數、柯里化等特性的基礎編程語言
回到上面那個函數,根據其輸入輸出的值,能夠造成以下的圖像:函數式編程
經過這個圖咱們能夠得出兩個結論:1. 函數必定有輸入和輸出值。 2. 函數對於相同的輸入值返回相同的輸出值,這個特性稱爲引用透明性。
引用透明性帶來的巨大好處在於併發代碼和結果可緩存,併發的問題在於對於全局變量的使用,須要引入鎖機制來管理全局變量的使用,可是引用透明性只依賴入餐,因此線程能夠自由運行。結果的可緩存也是顯而易見的,對於一個輸入其對應的輸出也是惟一的。因此該輸入的結果是能夠緩存的。
在引用透明性的基礎上咱們能夠定義純函數,純函數具備引用透明性,同時任何外部變量不會影響函數內部運算的結果,同時函數內部的運算也不會影響到外部變量的值。
純函數給咱們帶來的好處在於代碼的可測試性的加強,當咱們測試一個函數時,咱們會用輸入和指望的正確輸出值來驗證一個函數的正確性,可是當屢次測試經過以後,是否就能夠說咱們的函數是正確的,這個是不必定。若是外部的變量被應用在了函數的運算中,那麼當外部變量發生改變時,函數的計算結果也會變得不符合預期。同理,函數改變外部的值也會致使代碼的沒法測試。
與聲明式編碼相對應的是命令式代碼,意思就是,咱們經過編寫一條又一條指令去讓計算機執行一些動做,這其中通常都會涉及到不少繁雜的細節。
而聲明式就是,咱們經過寫表達式的方式來聲明咱們想幹什麼,而不是經過一步一步的指示。
舉個遍歷的例子,命令式編程的寫法是:
var array = [1,2,3];
for(var i = 0; i < array.length; i++){ console.log(array[i])
}複製代碼
而聲明式的寫法是:
array = [1, 2, 3];
array.forEach((item) => { console.log(item); })複製代碼
聲明式的好處在於代碼的編寫更清晰簡潔,在forEach函數爲純函數的前提下,咱們不用關心函數的內部是如何實現的,開發者只須要關注眼前的事情。
函數式編程根據上述的幾個特性,具備如下幾個優點:1. 代碼的可讀性更強 2. 代碼的可測試性更強 3. 易於編寫可緩存的代碼 4. 易於併發
因爲函數式編程採用聲明式的代碼,因此代碼的可讀性更強,舉個例子,當咱們進行一個這樣(1 + 2) * 3 - 4
的計算時,咱們可能會寫成subtract(multiply(add(1,2), 3), 4)
,對其進行聲明式的改造 add(1,2).multiply(3).subtract(4)
這樣咱們就能夠直觀的看出這句表達式的含義了。
這個優點是純函數帶來的,函數與外部變量的隔離使得測試只用關心函數內部邏輯,更易進行單元測試。
這兩個優點是函數的引用透明性帶來的,這裏再也不贅述。
首先清楚地理解什麼是函數:它不只僅是一個語句或者操做的集合,並且須要一個或多個輸入(理想狀況下只需一個!)和一個輸出。
函數是一等公民的這個特性是函數式編程的不少特性的基礎,也是Javascript語言函數式編程的基礎。
引用透明性和純函數使得函數更加易於測試,同時更易於併發編程和函數的緩存。
聲明式表達使得函數更易理解。
下一章咱們會開始討論如何使用函數式的思想進行編程。