個人github github.com/zhuanyongxi…javascript
這多是一篇會被常常改動的文章,它記錄瞭如今的我對函數式編程粗淺的理解。html
函數式編程並非github上面的一個工具庫,它的年齡比JavaScript要大得多,它是一種通過了幾十年,被衆多計算機科學家證實了的行之有效的編程範式。它不是學會了幾個函數式編程工具的API就能徹底掌握的,它更多的是編程思惟上的轉變。具體一點的代碼實例能夠看《JavaScript函數式編程之pointfree與聲明式編程》。java
那到底爲何用使用函數式編程?git
若是總結成一個字,那就是爲了「爽」。「爽」在哪?讀和寫。github
這可能會讓你不覺得然,讀和寫對你來講可能不是最重要的事情。不過以我當前的認知水平來看,設計模式、編程範式,甚至是編程語言自己,他們的最大意義都在於這兩個字。編程
好比編程語言,它的落點在於「語言」,「語言」是用來幹什麼的?交流溝通。跟誰交流溝通?跟人,而非機器。咱們寫的代碼會通過層層編譯,最終轉化成0和1才能被計算機執行。那咱們爲何要這麼麻煩,而不直接用0和1編程?由於很差寫也很差讀。若是你手速足夠快,大腦也足夠發達,用0和1編程也是能夠的。可那樣的話,還須要計算機嗎?雖然層層編譯的過程,勢必會下降效率,不過有摩爾定律來接盤,大可沒必要擔憂。設計模式
若是所有功能的實現只須要幾行代碼,大談設計模式、編程範式就太矯情了。咱們之因此須要這些東西,是由於隨着代碼量的增多,代碼的讀寫對咱們形成了困擾。數組
那函數式編程是如何解決這些問題的?編程語言
好比純函數,它有不少優勢,我的認爲最重要的是:它明顯的下降咱們的認知負荷。ide
從最普通的函數提及:
var a = 1;
function A(val) {
a = 2;
return a + val;
}
複製代碼
這裏的變量a就是被你們嗤之以鼻的全局變量。它很是容易致使出現bug,當代碼量不少的時候,極可能會由於重名而被覆蓋,從而致使其餘使用了這個變量的程序出錯。即使是正常的修改,當咱們再次使用函數A的時候,弄清變量a當前的狀態也是十分的困難,這就使得函數A變得難以理解,難以理解的函數就得不到使用者的信任。
面向對象編程的「封裝」在必定程度上解決了這個問題:
var obj = {
a: 1,
A: function(val) {
return this.a + val;
}
}
複製代碼
封裝了以後全局變量大大的減小了,即使有兩個對象重名了,相對上面的狀況,修改起來也不會有那麼大的負擔。但它並無完全解決這一問題,由於屬性a是可變的,每次調用方法A的時候,弄清屬性a的狀態依然不容易。
而函數式編程不存在這個問題。在函數式編程中,使用可變的外部的變量是不被容許的。例如:
var a = 1; // 除了當作參數傳入,使用變量a的函數都不是純函數
function A(val) { // 不是純函數
return a + val;
}
const b = 1;
const B = function(val) { // 純函數
return b + val;
}
複製代碼
隨着代碼量逐漸增多,這種寫法的優點就愈發明顯了,若是還能更進一步的減小耦合,那你就能夠在很大程度上去「斷章取義」,不去管上下文。每個純函數咱們均可以充分信任,它的輸出只與傳進去的參數有關。
不只僅在函數式編程中,面向對象的編程一般也會建議寫純函數。
純函數對代碼的調試也有極大的好處,這也是函數式編程的優勢之一:方便調試。
在初學函數式編程的時候,會很難理解這一點,由於當咱們使用純函數幫咱們解決問題的時候,老是得不到想要的結果,尤爲是在使用函數式的工具庫進行函數組合的時候,斷點調試都很困難,就算你把斷點打到了第三方的工具庫裏面,你真的肯定你能在短期內看懂人家的源碼嗎?這個時候每每只能想辦法去console.log
。不過這個問題是能夠經過不斷的學習總結來解決的。即使使用其餘的方式編程,不熟悉所用的工具也會出現一樣的狀況,這並非函數式編程的問題。
絕大多數的bug都是由反作用引發的。也就是系統狀態的變化。這一點沒法經過學習來解決,你能記住成千上萬個狀態,並能時刻了解他們的變化嗎?可若是咱們是在函數式編程,使用了大量的純函數,因爲純函數不產生反作用,在調試的時候,咱們只須要去調試會產生反作用的部分就能夠了。
在函數式編程中,一切的技巧的目的都是爲了讓你的代碼更加的函數式。可掌握有些技巧的成本很高,這就致使部分人對函數式編程的」可讀性「產生了質疑。
其實,函數式代碼的可讀性與讀代碼的人的函數式編程的能力有直接的關係。以下圖:
命令式編程很符合人類的思惟習慣,入門的成本很低,而聲明式的函數式編程的學習則有必定的門檻,對於新手來講,可能幾乎沒有可讀性。只有不斷的深刻學習,這種可讀性的優點纔會逐漸的體現出來。這多是致使函數式編程沒有被普遍應用的最主要緣由。
若是還要舉的話。。。
面向對象編程更傾向於封裝,而函數式編程更傾向於抽象。
面向對象編程抽象的結果是一個類,函數式編程抽象的結果是一個過程(一個函數)。
封裝和抽象有什麼區別?
他們並無明顯的界限,抽象是經過封裝來實現的,我找不到一個只是抽象而不是封裝的例子。封裝和抽象的目的略有不一樣。簡單的講,封裝只是把一些會再次使用的操做包起來,等待下次使用。抽象也會有「包起來」的動做,可它更多的目的在於要劃清界限。這就比如讓咱們解釋一個概念,若是你說「我懂,但我就是說不出來」,那麼大機率你是沒有懂。咱們是否能清晰準確的說清楚,取決於咱們腦中對這一律唸的界限是否劃清楚了。若是沒有劃清楚,咱們就沒辦法下斷語去說明它是什麼和它不是什麼。抽象也是如此,它須要咱們搞清楚它是幹什麼的,把不該該乾的事情都去掉。「它是什麼」這一點越明確,那麼這一封裝的抽象程度就越高。
參考資料: