淺談函數式編程和函數響應式編程

大概幾個月前,受題葉影響開始關注函數式編程,在看完一本 js 的函數式以後,對函數式有了點基本的瞭解。前端

函數式給個人感受,最大的一個主旨是是編程中全部過程可控,尤爲在 js 這種沒有原則的語言中,過程可控制有爲重要。git

函數式

到底什麼是函數式,他和命令式編程和麪向對象有什麼區別。(知乎上已經有不少討論了,感興趣的話,我在結尾的地方貼了一些連接。)
總的來講,在函數式中,函數是一等公民,函數能做爲變量的值,函數能夠是另外一個函數的參數,函數能夠返回另外一個函數等等。github

函數式中有些我的感受比較有意思的點編程

純函數

最喜歡純函數了,什麼是純函數呢?純函數有三個重要的點:promise

  1. 函數的結果只受函數參數影響。
  2. 函數內部不使用能被外部函數影響的變量。
  3. 函數的結果不影響外部變量。

這有什麼好處呢?當你有一堆函數對某個數據進行操做的時候,就能輕鬆定位到哪些函數出了問題,這也就是 Redux 的中心思想,控制狀態的 Reducer 就是一個純函數。函數式編程

不可變

說到純函數,不得不聊聊不可變。函數

FB 在推出 React 以後, immutable.js 也跟着火起來了。不可變,顧名思義,就是變量或者結構在定義以後不能再發生值的變更,全部操做只是產生新的值而不是去覆蓋以前的變量。這樣去控制數據,可以讓數據流動更加可控。this

在 jQuery 大行其道的代碼中,最爲人稱讚的莫過於其鏈式操做了,但不知道有沒有人跟我同樣遇到過一些問題,好比我對一個節點進行操做,搞着搞着當前節點不是這個節點了,當我須要對這個節點再進行一次操做的時候,只能新開一個鏈式。spa

在函數中,流永遠只是對當前的那個初始的數據進行操做,並且當加上惰性鏈,全部過程更加容易控制。code

什麼是惰性鏈?

回想一下 jq 的鏈式操做,是否是每次一個連接加上去就立馬生效,當你寫完一堆鏈式以後,發現不對,若是不能一眼看出是哪步錯了,只能一次次去刪最後的操做來找緣由。而惰性鏈是在你寫完鏈式時候並不會執行,而是在最後跟上一個執行用的函數,纔會去執行前面的全部函數。

mixins

打個比方,當咱們須要類型判斷的時候,紅皮書裏通常都是各類 if,不知道平時寫的時候也是這樣,用個 utils 寫各類類型判斷的函數,而後挨個 if。

用 mixins 思路的話,大概能夠這麼去作:

dispach(
function(s){ return isString(s) ? s : undefined },
function(s){ return isArray(s) ? stringifyArray(s) : undefined },
function(s){ return isObject(s) ? stringfyObject(s) : undefined },
function(s){ return s.toString() }
)

這樣只要有個函數返回的確定是字符串了。

函數響應式編程

響應式編程(反應式編程)

響應式編程是一種面向數據流和變化傳播的編程範式,數據更新是相關聯的。好比不少時候,在寫界面的時候,咱們須要對事件作處理,伴隨着前端事件的增多,對於事件的處理愈發須要更加方便的處理。

設想一下,平時在處理事件的時候,一單上了複雜度,好比輸入的時候,須要中止輸入的時候才進行,這個時候又只能輸入長度大於2才進行事件,當仍是以前的數據的話不進行事件,能夠考慮一下這個狀況下如何去寫。

拿 RxJs 中的一端例子舉例

var keyup = Rx.Observable.fromEvent($input, 'keyup')
      .map(function (e) {
        return e.target.value; 
      })
      .filter(function (text) {
        return text.length > 2; 
      })
      .debounce(750)
      .distinctUntilChanged();

是否十分簡潔。

把函數範式裏的一套思路和響應式編程合起來就是函數響應式編程。

不知道有沒有發現其實 Promise 也是響應式編程的一種。

舉個寒冬大大的代碼,在一個按鈕上綁定兩個事件,一個是 5s 後觸發,一個是用戶點擊。

function wait(duration){
    return new Promise(function(resolve, reject) {
        setTimeout(resolve,duration);
    })
}

function waitFor(element,event,useCapture){
    return new Promise(function(resolve, reject) {
        element.addEventListener(event,function listener(event){
            resolve(event)
            this.removeEventListener(event, listener, useCapture);
        },useCapture)
    })
}

var btn = document.getElementById('button');
Promise.race([wait(5000), waitFor(btn, click)]).then(function(){
    console.log('run!')
})

把兩個事件綁定到按鈕上,這兩個事件函數同時也是 promise,這樣只要有一個觸發了就會執行,相比普通代碼

btn.addEventListener(event, eventHandler, false);
setTimeout(eventHandler, 5000);
function eventHandler(){
  console.log('run!');
}

當須要增長更多事件組合的時候,更加容易拓展。

剛纔的代碼,用 Rx.js 實現會更加簡潔

var btn = document.getElementById('button');
var logRun = Rx.Observable.fromEvent(btn, 'click')
             .merge(Rx.Observable.timer(3000))
             .subscribe(e => {
               console.log('run!');
               logRun.dispose(); // 若是是一次性的就移除observable
             });

先這樣,如有不對的地方請告知,感謝~

相關文章
相關標籤/搜索