javascript函數語言程式設計: 優雅的使用underscore進行函式程式設計

莊文達發表於2017-10-19

一等公民

函式是一等公民: 所謂一等公民①顧名思義身份高,JS任何只要是值能到達的地方,函式都可以去。

字串是一等公民,那麼函式也可以擁有字串一樣的特質


var str = function(){return '123'} //儲存變數

var array = ['123',function(){return '456'}]//做陣列成員

var concatStr = '123'+function(){return '456'}() //拼接

function concatFun (str,fn){
    return str + fn();
}
concatFun(123,function(){return '456'}) //做引數

return function(){return '123'} //被返回

複製程式碼

正向我們上一文使用call、apply舉例高階函式應該具備的特性:

  • 可以以函式作為引數

  • 可以返回函式

這正是underscore被廣泛應用的原因

操場報數遊戲

體育老師上課打太極前找同學集合點到:

學生總數10人,報數順序自行定義:

於是我們寫出瞭如下程式碼:

    (function () {
        var numberBill = [] //報數名單
        for (var number = 0; number < 10; number++) {
            numberBill.push('同學 ' + number + ' 報數');
            if (number == 10) console.log('沒有學生了');
            if (number < 10) console.log('還剩:' + (10 - number - 1) + '名學生沒有報數');
        }
    })()
    
複製程式碼

結果:

VM85068:6 還剩:9名學生沒有報數
VM85068:6 還剩:8名學生沒有報數
VM85068:6 還剩:7名學生沒有報數
VM85068:6 還剩:6名學生沒有報數
VM85068:6 還剩:5名學生沒有報數
VM85068:6 還剩:4名學生沒有報數
VM85068:6 還剩:3名學生沒有報數
VM85068:6 還剩:2名學生沒有報數
VM85068:6 還剩:1名學生沒有報數
VM85068:6 還剩:0名學生沒有報數
複製程式碼

這種程式設計方式很常見,誰都會寫,我們一般叫這種程式設計方式稱之為命令程式設計②,這個邏輯完全在你掌握之中,你只要規定計算機在你想要的時候執行一些不同的動作就可以了。

下文中出現的註解underscore方法會在文章最後註解講解,複雜的單獨抽離幾張講解。

   function signInPositive(number) { //正序簽到
        var count = 1;
        return _.chain([])//①chain幹什麼的
                .push('同學 ' + count + ' 正常報數')
                .tap(function (numberBill) { //②numberBill哪來的?
                    _.each(_.range(number),function(v,k){
                        if (count < number) numberBill.push('同學 ' + (count++) + ' 正常報數')
                        console.log('還有'+(number-count)+'名同學沒報數');
                    })
                }).value();//③
    }
    signInPositive(10)

複製程式碼

雖然我們勉強用函式程式設計實現了,但是有啥好處呢?沒啥好處,最起碼這個實現方法沒有凸顯出函式程式設計的價值,還不如命令程式設計容易理解。


我記得去年摩托羅拉出了一個模組化手機,他的攝像頭,電池,外殼貌似都能拆卸換配件的,函數語言程式設計也是這樣,我們把可以抽象抽象出來,再進行組合,進行模組化


上面的例子迴圈是從(0--傳入的數字)正序迴圈,若是我不想從0開始呢?若是我倒序迴圈隨機迴圈呢?

改一下先把_.tap內部的_each迴圈去掉

 

function signIn(number) { //簽到
        return _.chain([])//①chain幹什麼的?見文章底部
                /*.push('同學 ' + number + ' 正常報數')*/
                .tap(function (numberBill) { //②numberBill哪來的?見文章底部
                     numberBill.push('同學 ' + (number+1) + ' 正常報數')
                     console.log(('同學 ' + (number+1) + ' 正常報數'))
                    //我故意不在體內迴圈
                }).value();//③value幹啥的?見文章底部
    }

    
複製程式碼

另外說明:

上文中
_.chain([])/*.push('同學 ' + number + ' 正常報數')
複製程式碼

這個鏈式操作加上push是為了讓大家理解_.chain函式的作用,因為我在underscore文件說過這樣句話:

javascript函數語言程式設計: 優雅的使用underscore進行函式程式設計

說的很明白了,我們可以使用_.chain鏈式呼叫的同時,也支援JS 原生Arrayprototype中的push操作,並且知道你呼叫value()這些後面講,不要耽誤大家的思路。

那麼這個方法改成這樣負責什麼職能呢?


就是負責儲存報數的同學生成點名單,並不在乎你怎麼迴圈,我只負責儲存,所以因為我們為了靈活性模組化吧內部的each操作剔除了,所以我們必然要在其他地方實現遍歷,而且最好能支援自定義迴圈...還要能儲存上面方法返回的資料...


於是乎我想到了java中的模板引擎,比如jspfreemaker等,有這種設定startend以及步長的操作,通過步長:12-1-2迴圈,按照這個思路造個支援函式的輪子,不是剛好可以解決這個問題嘛?

但是我們彆著急,若果你瞭解underscore你會知道,這種東西你找它就好了!

  • 運氣不錯

哦?運氣真好,不小心找到兩個函式

javascript函數語言程式設計: 優雅的使用underscore進行函式程式設計

javascript函數語言程式設計: 優雅的使用underscore進行函式程式設計

我們可以用_.range_.reduce搭配使用,用_.range假冒一個陣列,搭配reduce迭代,模擬出了一個類似模板引擎步長迭代器之類的東西。 是不是激起了你寫JS模板引擎的慾望呢? --別傻了,js不需要,即便需要類似grunt中的JST外掛或者Underscore _.Templates也完全夠用了,現在是個公司都搞前後分離模板引擎只適合部落格小專案,或者大公司靜態化了...

先寫個DEMO測試一下:

  var sum = _.reduce(_.range(1,10,1), function(memo, num){ return memo + num; }, 0
  
  這樣我們就寫出了1-10的累加,連迴圈都沒寫
  
  ps:( 我以前看到個文章:30天不使用for迴圈 )
  
複製程式碼
  • 歐耶

沒讓您失望吧,其實對於underscorelodash中的幾個重要函式什麼防抖節流閥...我想深入開幾張講解的,正好我也深入研究一下,畢竟好姿勢希望大家都能用上。


    function startPositive(start,end,signInFn){
        return _.reduce(
                _.range(start,end,1),
                function(acc,n){
                    return acc.concat(signInFn(n));
                },[])
    }

    var peapleArr = startPositive(0,10,signIn) //最終報數名單
    
複製程式碼

完成了

做了什麼事?

我們成功把耦合函式內部的迴圈抽離了,分為了兩個函式, 主函式寫名單陣列,複函式提供喊到方法並且拿到陣列

  • 好處? 顯而易見,你的喊道方式再也不用修改主函式了,你只要多寫幾個如:startInversestartRandom複函式提供方法即可

若你需求更加複雜或者更加優雅,你可以把startPositive也進行抽離,改變步長達到正序逆序隨機的效果。


① 幾乎JS的書籍都提到這個術語,現在java8都積極響應函式程式設計,但是有些人依然認為函式思想不如OO思想,評論區撕逼,管他呢,存在即合理。

② 命令程式設計,機器語言if else case控制,流程都是你每行程式碼進行控制.而函式程式設計更加自由、優雅。


文中出現的underscore函式

  • _.chain
chain_.chain(obj) 
返回一個封裝的物件. 在封裝的物件上呼叫方法會返回封裝的物件本身, 
直道 value 方法呼叫為止.
複製程式碼

這個函式就是為了優雅的使用鏈式呼叫準備的,value很簡單拿到最終值,畢竟鏈式呼叫的原理是返回物件本身,設計模式第一部分我就講了

javascript函數語言程式設計: 優雅的使用underscore進行函式程式設計

javascript函數語言程式設計: 優雅的使用underscore進行函式程式設計
粗略通過原始碼我們知道underscore提供一個value返回真正的資料,並且return _wrapped返回原始資料結束

  • _.tap
tap_.tap(object, interceptor) 
用 object作為引數來呼叫函式interceptor,然後返回object。這種方法的主要意圖是作為函式鏈式呼叫 的一環, 為了對此物件執行操作並返回物件本身。
複製程式碼

可能有些人疑惑?為啥你的

.tap(function (numberBill) { 
複製程式碼

numberBill哪來的?

javascript函數語言程式設計: 優雅的使用underscore進行函式程式設計

通過原始碼我們就知道其實是他利用回撥將我們傳入的[]返回的。

  • _.reduce

剛才已經講了使用方法,涉及到函式程式設計,我會後期單獨出幾章,並且仿造輪子

本文github原始碼有部分註釋,搭配看,更加清晰。

javascript函數語言程式設計: 優雅的使用underscore進行函式程式設計

  • 看情況百度 其實不懂得東西沒那麼可怕,我們只有不斷學習才能進步,不懂的東西不要百度,儘量看看官網API,看一手資源。

  • 本文 -- 首發github

  • 開源交流群:147255248

相關文章