一等公民
函數是一等公民: 所謂一等公民①
顧名思義身份高,JS
任何只要是值能到達的地方,函數均可以去。html
字符串是一等公民,那麼函數也能夠擁有字符串同樣的特質java
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舉例,高級函數
應該具有的特性:node
能夠以函數
做爲參數git
能夠返回函數
github
這正是underscore
被普遍應用的緣由編程
體育老師上課打太極前找同窗集合點到:設計模式
學生總數10
人,報數順序自行定義:數組
因而咱們寫出了以下代碼:bash
(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) + '名學生沒有報數');
}
})()
複製代碼
結果:app
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
文檔說過這樣句話:
說的很明白了,咱們可使用_.chain
鏈式調用的同時,也支持JS
原生Array
的prototype
中的push
操做,而且知道你調用value()
這些後面講,不要耽誤你們的思路。
那麼這個方法改爲這樣負責什麼職能呢?
就是負責存儲報數的同窗生成點名單,並不在意你怎麼循環,我只負責保存,因此由於咱們爲了靈活性
和模塊化
吧內部的each
操做剔除了,因此咱們必然要在其餘地方實現遍歷,並且最好能支持自定義循環
...還要能保存上面方法返回的數據...
因而乎我想到了java
中的模板引擎
,好比jsp
,freemaker
等,有這種設置start
和end
以及步長
的操做,經過步長
:1
,2
,-1
,-2
循環,按照這個思路造個支持函數
的輪子,不是恰好能夠解決這個問題嘛?
可是咱們彆着急,若果你瞭解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循環 )
複製代碼
沒讓您失望吧,其實對於underscore
和lodash
中的幾個重要函數什麼防抖
、節流閥
...我想深刻開幾張講解的,正好我也深刻研究一下,畢竟好姿式
但願你們都能用上。
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) //最終報數名單
複製代碼
完成了
作了什麼事?
咱們成功把耦合函數內部的循環抽離了,分爲了兩個函數, 主函數寫名單數組,複函數提供喊到
方法而且拿到數組
startInverse
、startRandom
複函數提供方法便可若你需求更加複雜或者更加優雅,你能夠把startPositive
也進行抽離,改變步長達到正序
、逆序
、隨機
的效果。
① 幾乎JS
的書籍都提到這個術語,如今java8都積極響應函數編程,可是有些人依然認爲函數思想不如OO思想,評論區撕逼,管他呢,存在即合理。
② 命令編程,機器語言if else
case
控制,流程都是你每行代碼進行控制.而函數編程更加自由、優雅。
chain_.chain(obj)
返回一個封裝的對象. 在封裝的對象上調用方法會返回封裝的對象自己,
直道 value 方法調用爲止.
複製代碼
這個函數就是爲了優雅的使用鏈式調用準備的,value很簡單拿到最終值,畢竟鏈式調用的原理是返回對象自己,設計模式第一部分我就講了
underscore
提供一個
value
返回真正的數據,而且
return
_wrapped
返回原始數據結束
tap_.tap(object, interceptor)
用 object做爲參數來調用函數interceptor,而後返回object。這種方法的主要意圖是做爲函數鏈式調用 的一環, 爲了對此對象執行操做並返回對象自己。
複製代碼
可能有些人疑惑?爲啥你的
.tap(function (numberBill) {
複製代碼
numberBill
哪來的?
經過源碼咱們就知道實際上是他利用回調將咱們傳入的[]
返回的。
剛纔已經講了使用方法,涉及到函數編程,我會後期單獨出幾章,而且仿造輪子
本文github源碼有部分註釋,搭配看,更加清晰。
看狀況百度 其實不懂得東西沒那麼可怕,咱們只有不斷學習才能進步,不懂的東西不要百度,儘可能看看官網API,看一手資源。
本文 -- 首發github
開源交流羣:147255248