失落迷茫了好一段日子。終於我用接觸2個月的技術Nodejs成功的混到一份工做。嚴格來講只學習了3天(白天睡覺,晚上通宵學習),後面的時間都是在配置環境。總的來講,函數式編程是有應用的市場的,並且學習門檻也不是過高。就算曆來沒據說過函數式編程的人也會知道javascript,也會使用jquery。雖然不少是把它看成過程式的來用,來看待。這也是在於它的語法看起來太像C,太像過程式的語言。javascript
以前一直想寫一些關於函數編程文章來記錄我學習的歷程。以前寫了一篇使用F#的,不過你們好像對F#比較排斥。之後我從工做出發寫nodejs的吧。css
好了。廢話很少說咱們先從一個具體的項目來分析函數式編程吧。前端
用webstorm新建一個express項目,這是nodejs下用來作web服務器的庫。會生成相似下面這個結構的文件。java
總的來講文件結構只是約定俗成,或是按人們習慣來用的。不像java、C之類的會有main函數做爲入口。任何文件都能看成啓動入口。nodejs也不只限於開發web服務器,加上各類奇葩的中間件的運用,會讓項目變成各類形態。這是一個自由度很是高的開發平臺。node
咱們先寫一個簡單的demo。因爲js的語法太過糾結,咱們使用另一種語言coffeescript,他是一個nodejs的庫。能本身運行在nodejs上,也能編譯成js文件。這裏咱們只是用作語法糖,仍然編譯成js文件。我會貼出兩種代碼來適應不一樣的須要。jquery
coffeescriptgit
fna = -> console.log("I am 'a'") fnb = -> console.log "I am 'b'" fna() fnb()
javscriptgithub
// Generated by CoffeeScript 1.7.1 (function() { var fna, fnb; fna = function() { return console.log("I am 'a'"); }; fnb = function() { return console.log("I am 'b'"); }; fna(); fnb(); }).call(this); //# sourceMappingURL=test.map
這裏我編寫了兩個函數,並依次調用它們。coffeescript會嚴格申明變量和閉包,不會讓其污染全局變量。代碼精簡很多,看起來也更像是函數式編程了。輸入結果顯而易見。web
console.logexpress
i am 'a'
i am 'b'
nodejs是異步執行的。若是這是兩個有關聯的函數呢?
coffeescript
fna = -> console.log("這是母雞") fnb = -> console.log "母雞下蛋" fna() fnb()
javascript
// Generated by CoffeeScript 1.7.1 (function() { var fna, fnb; fna = function() { return console.log("這是母雞"); }; fnb = function() { return console.log("母雞下蛋"); }; fna(); fnb(); }).call(this); //# sourceMappingURL=test.map
單從結果來看,好像沒有什麼問題。
console.log
這是母雞
母雞下蛋
在實際項目中,咱們並不知道兩個函數內部到底幹了什麼,就像蝴蝶效應,任何改動均可能讓結果發生變更。
coffeescript
fna = -> setTimeout -> console.log("這是母雞") , 100 fnb = -> console.log "母雞下蛋" fna() fnb()
javascript
// Generated by CoffeeScript 1.7.1 (function() { var fna, fnb; fna = function() { return setTimeout(function() { return console.log("這是母雞"); }, 100); }; fnb = function() { return console.log("母雞下蛋"); }; fna(); fnb(); }).call(this); //# sourceMappingURL=test.map
console.log
母雞下蛋
這是母雞
如今就不是咱們想要的結果了。其實這種異步方式也很好理解,它只管函數調用,而無論函數結果。在同步編程中,前一步操做會阻塞後一步操做,母雞下蛋的操做會等着這隻母雞出結果。而異步編程中,不會阻塞後面的任務進行,就像指揮官給手下發派任務,手下都會去執行各自的任務,但何時完成任務就很差說了。這樣作的好處就是在執行耗時任務的時候,其餘的任務也能繼續執行,或者同時執行多個耗時任務。可是有利有弊,在流程控制上會比較糾結。常規作法是用回調函數,就像有人說過,世上原本沒有回調,用的人多了也就有了回調函數。
coffeescript
fna = (next) -> setTimeout -> console.log("這是母雞") next() , 1000 fnb = -> console.log "母雞下蛋" fna -> fnb()
javascript
// Generated by CoffeeScript 1.7.1 (function() { var fna, fnb; fna = function(next) { return setTimeout(function() { console.log("這是母雞"); return next(); }, 1000); }; fnb = function() { return console.log("母雞下蛋"); }; fna(function() { return fnb(); }); }).call(this); //# sourceMappingURL=test.map
conslole.log
這是母雞
母雞下蛋
這中方法雖然解決了關聯函數的流程控制問題,可是也有新的問題。邏輯複雜的時候,回調嵌套就會愈來愈深。
coffeescript
fna = (next) -> setTimeout -> console.log("這是母雞") next() , 1000 fnb = (next) -> setTimeout -> console.log "母雞下蛋" next() , 100 fnc = -> console.log "蛋孵出了雞" fna -> fnb -> fnc()
javascript
// Generated by CoffeeScript 1.7.1 (function() { var fna, fnb, fnc; fna = function(next) { return setTimeout(function() { console.log("這是母雞"); return next(); }, 1000); }; fnb = function(next) { return setTimeout(function() { console.log("母雞下蛋"); return next(); }, 100); }; fnc = function() { return console.log("蛋孵出了雞"); }; fna(function() { return fnb(function() { return fnc(); }); }); }).call(this); //# sourceMappingURL=test.map
console.log
這是母雞
母雞下蛋
蛋孵出了雞
幸虧有中間件解決這個問題。async 中間件有各類流程控制方法。其中series就能很優美的實現這個邏輯。你所要作的就是每一個函數里加上一個回調next執行下一步操做,第一個參數是err,第二個參數能追加一個結果,在async最後的回調中返回出來。
coffeescript
async = require "async" fna = (next) -> setTimeout -> console.log "這是母雞" next(null, 1) , 1000 fnb = (next) -> setTimeout -> console.log "母雞下蛋" next(null, 2) , 2000 fnc = (next) -> setTimeout -> console.log "蛋孵出了雞" next(null, 3) , 100 async.series [ fna fnb fnc ] , (err, results) -> console.log results
javascript
// Generated by CoffeeScript 1.7.1 (function() { var async, fna, fnb, fnc; async = require("async"); fna = function(next) { return setTimeout(function() { console.log("這是母雞"); return next(null, 1); }, 1000); }; fnb = function(next) { return setTimeout(function() { console.log("母雞下蛋"); return next(null, 2); }, 2000); }; fnc = function(next) { return setTimeout(function() { console.log("蛋孵出了雞"); return next(null, 3); }, 100); }; async.series([fna, fnb, fnc], function(err, results) { return console.log(results); }); }).call(this); //# sourceMappingURL=test.map
console.log
這是母雞
母雞下蛋
蛋孵出了雞
[ 1, 2, 3 ]
更好的封裝,應該是這個樣子。
coffeescript
async = require "async" fna = (next) -> setTimeout -> console.log "這是母雞" next() , 1000 fnb = (next) -> setTimeout -> console.log "母雞下蛋" next() , 2000 fnc = (next) -> setTimeout -> console.log "蛋孵出了雞" next() , 100 async.series [ (next) -> fna -> next null, 1 (next) -> fnb -> next null, 2 (next) -> fnc -> next null, 3 ] , (err, results) -> console.log results
javascript
// Generated by CoffeeScript 1.7.1 (function() { var async, fna, fnb, fnc; async = require("async"); fna = function(next) { return setTimeout(function() { console.log("這是母雞"); return next(); }, 1000); }; fnb = function(next) { return setTimeout(function() { console.log("母雞下蛋"); return next(); }, 2000); }; fnc = function(next) { return setTimeout(function() { console.log("蛋孵出了雞"); return next(); }, 100); }; async.series([ function(next) { return fna(function() { return next(null, 1); }); }, function(next) { return fnb(function() { return next(null, 2); }); }, function(next) { return fnc(function() { return next(null, 3); }); } ], function(err, results) { return console.log(results); }); }).call(this); //# sourceMappingURL=test.map
到這裏coffeescript還能夠一戰,js你已經徹底看不懂了對不對?
今天就寫到這裏了,我接觸到的範圍也不廣,之後你們有什麼關於函數式編程的問題能夠告知,你們一塊兒解決。