JavaScript 函數式編程究竟是個啥

隨着大前端時代的到來,在產品開發過程當中,前端所佔業務比重愈來愈大、交互愈來愈重。傳統的老夫拿起JQuery就是一把梭應付當下重交互頁面已經十分乏力。因而乎有了Angular,React,Vue這些現代框架。javascript

但隨之而來的還有大量的新知識新名詞,如MVC,MVVM,Flux這些設計模式就弄得不少同窗傻傻分不清。這時候又見到別人討論什麼函數式編程,更是一臉懵逼了。html

咱們大多聽過面向對象編程,面向過程編程,那啥又是函數式編程呢?在咱們前端開發中又有哪些應用場景?我抱着這個疑惑,初步的學習了下。(此文僅是學習,無甚乾貨)。前端

函數式編程

定義

函數式編程(Functional Programming,後面簡稱FP),維基百科的定義是:java

是一種編程範型,它將電腦運算視爲數學上的函數計算,而且避免使用程序狀態以及易變對象。函數編程語言最重要的基礎是λ演算(lambda calculus)。並且λ演算的函數能夠接受函數看成輸入(引數)和輸出(傳出值)。比起命令式編程,函數式編程更增強調程序執行的結果而非執行的過程,倡導利用若干簡單的執行單元讓計算結果不斷漸進,逐層推導複雜的運算,而不是設計一個複雜的執行過程。react

我來嘗試理解下這個定義,好像就是說,在敲代碼的時候,我要把過程邏輯寫成函數,定義好輸入參數,只關心它的輸出結果。並且能夠把函數做爲輸入輸出。感受好像日常寫js時,就是這樣的嘛!編程

特性

網上FP的定義與特性琳琅滿目。各類百科、博客、一些老師的網站上都有大同小異的介紹。爲了方便閱讀,我列下幾個好像比較重要的特性,並附上個人第一眼理解。設計模式

  1. 函數是一等公民。就是說函數能夠跟其餘變量同樣,能夠做爲其餘函數的輸入輸出。喔,回調函數就是典型應用。數組

  2. 不可變量。就是說,不能用var跟let咯。按這要求,我彷佛有點難寫代碼。閉包

  3. 純函數。就是沒有反作用的函數。這個好理解,就是不修改函數外部的變量。框架

  4. 引用透明。這個也好理解,就是說一樣的輸入,一定是一樣的輸出。函數內部不依賴外部狀態,如一些全局變量。

  5. 惰性計算。大意就是:一個表達式綁定的變量,不是聲明的時候就計算出來,而是真正用到它的時候纔去計算。

還有一些衍生的特性,如柯里化與組合,三言兩語說不清,就不闡述了,有興趣的同窗能夠本身再瞭解瞭解。

FP在JavaScript中的應用

React就是典型的FP。它不一樣於Vue這樣的MVVM框架,它僅僅是個View層。
ReactView = render(data) 它只關心你的輸入,最終給你返回相應視圖。因此你休想在react組件中去修改父組件的狀態,更沒有與dom的雙向綁定。

這個是框架上的應用,那麼在咱們日常書寫JavaScript時有哪些應用呢?換句話說,日常書寫js時候,遇到什麼狀況,咱們採用FP會更好。

從最多見的入手吧,如典型的操做數組:

// 從users中篩選出年齡大於15歲的人的名字
const users = [
  {
    age: 10,
    name: '張三',
  }, {
    age: 20,
    name: '李四'
  }, {
    age: 30,
    name: '王五'
  }
];

// 過程式
const names = [];
for (let i = 0; i < users.length; i++) {
  if (users[i].age > 15) {
    names.push(users[i].name);
  }
}
// 函數式
const names = users.filter(u => u.age > 15).map(u => u.name);

嗯,代碼精簡了不少,可是貌似帶來了更大的開銷。若是是很是大的數據,很是多的篩選工做,那就會循環屢次。

這裏得想到剛剛的惰性計算。按照惰性求值的要求,應該是要最後返回結果時,才真正去篩選年紀並獲得姓名數組。

然而JavaScript的數組並不支持惰性求值。這時候咱們得上一些工具庫,如Lodash。能夠看下它文檔中的例子:_.chain

好像也沒好到哪裏去啊,不就是把多行代碼變一行嘛?說的那麼玄乎,還多了性能開銷,而後又跟我說得上個工具庫。。。

說的好像頗有道理,可是for循環是有個弊端的,它產生了變量i,而這個變量又是不可控的,若是業務邏輯一複雜,誰知道它循環到何時i有沒有發生變化,而後致使循環出問題呢?

咱們再看一個與DOM交互的場景:
假如頁面有一個按鈕button,咱們須要求出用戶點擊了幾回,可是一秒鐘內重複點擊的不算。傳統方法會這麼寫。

var count = 0;
var rate = 1000;
var lastClick = Date.now() - rate;
var button = document.querySelector('button');
button.addEventListener('click', () => {
  if (Date.now() - lastClick >= rate) {
    console.log(`Clicked ${++count} times`);
    lastClick = Date.now();
  }
});

妥,徹底沒問題。可是發現多了不少狀態,count,rate,lastClick,還得對比來對比去。那若是用FP會是怎麼樣的呢?

抱歉。。。無法寫。。。除非很強大的編程能力,本身封裝好方法去處理。因此在這裏,咱們能夠上個工具---Rx.js,上述的例子就是rxjs中引用的,咱們看它是如何優雅地處理的。

var button = document.querySelector('button');
Rx.Observable.fromEvent(button, 'click')
  .throttleTime(1000) // 每隔1000毫秒才能觸發事件
  .scan(count => count + 1, 0) // 求值,默認值是0
  .subscribe(count => console.log(`Clicked ${count} times`)); // 訂閱結果、輸出值

巧奪天工!不再用去管理狀態了,不須要聲明一堆變量,修改來修改去,判斷來判斷去,簡直完美。

日常咱們有不少須要更新dom的異步操做,如搜索行爲:用戶連續輸入查詢值,若是停頓半秒就執行搜索,若是搜索了屢次,發起了屢次請求,那隻返回最終輸入的那次搜索結果。

閉上眼想一想,你以前是怎麼實現的。反正我都是設置開始時間,結束時間,上次時間,等等變量。繁瑣,並且不可控。

當咱們以FP的思想去實現時,就會千方百計的減小變量,來優雅程序。最多見的方法就是用下別人的工具庫來實現它。固然有些簡單的場景也能夠本身實現,最主要的仍是要有這個意識。

其實咱們日常已經寫了一些FP了,只是咱們沒意識到,或者沒怎麼寫好。就比如閉包,不少人都不瞭解閉包的概念,但實際上已經寫了不少閉包代碼。其實閉包自己也是函數式編程的一個應用。

鑑於我本身理解也不深,無法多闡述FP的應用,你們若是有興趣,能夠多瞭解瞭解。

FP在JavaScript中的優劣勢

總結一下FP的優劣,以便於咱們在實際開發中,能更好的抉擇是否採用FP。

優點

  1. 更好的管理狀態。由於它的宗旨是無狀態,或者說更少的狀態。而日常DOM的開發中,由於DOM的視覺呈現依託於狀態變化,因此不可避免的產生了很是多的狀態,並且不一樣組件可能還相互依賴。以FP來編程,能最大化的減小這些未知、優化代碼、減小出錯狀況。

  2. 更簡單的複用。極端的FP代碼應該是每一行代碼都是一個函數,固然咱們不須要這麼極端。咱們儘可能的把過程邏輯以更純的函數來實現,固定輸入->固定輸出,沒有其餘外部變量影響,而且無反作用。這樣代碼複用時,徹底不須要考慮它的內部實現和外部影響。

  3. 更優雅的組合。往大的說,網頁是由各個組件組成的。往小的說,一個函數也多是由多個小函數組成的。參考上面第二點,更強的複用性,帶來更強大的組合性。

  4. 隱性好處。減小代碼量,提升維護性。

劣勢

  1. JavaScript不能算是嚴格意義上的函數式語言,不少函數式編程的特性並無。好比上文說的數組的惰性鏈求值。爲了實現它就得上工具庫,或者本身封裝實現,提升了代碼編寫成本。

  2. 跟過程式相比,它並無提升性能。有些地方,若是強制用FP去寫,因爲沒有中間變量,還可能會下降性能。

  3. 代碼不易讀。這個因人而異,因碼而已。特別熟悉FP的人可能會以爲這段代碼一目瞭然。而不熟悉的人,遇到寫的晦澀的代碼,看着一堆堆lambda演算跟匿名函數 () => () => () 瞬間就懵逼了。看懂代碼,得腦子裏先演算半小時。

  4. 學習成本高。一方面繼承於上一點。另外一方面,不少前端coder,就是由於相對不喜歡一些底層的抽象的編程語言,纔來踏入前端坑,你如今又讓他們一頭扎入FP,顯得手足無措。

總結

我的以爲,FP仍是好的。對於開發而言,確確實實能優化咱們的代碼,熟悉以後,也能提升編程效率。對於編程自己而言,也能拓展咱們的思惟,不侷限在過程式的編程代碼。

在編寫JS中,能夠儘可能的運用FP的思惟,如不可變量、純函數、惰性求值。但也沒必要教條式的遵循函數式編程,必定要怎樣怎樣。好比咱們看下知乎大V某溫的一個回答:傳送門

唉,作個頁面仔不容易啊。可是不想當大牛的頁面仔不是好頁面仔!

參考

  1. 函數式編程入門教程-阮一峯

  2. 函數編程語言-維基百科

  3. 前端開發js函數式編程真實用途體如今哪裏?-知乎答者

相關文章
相關標籤/搜索