關於譯者:這是一個流淌著滬江血液的純粹工程:認真,是 HTML 最堅實的樑柱;分享,是 CSS 裡最閃耀的一瞥;總結,是 JavaScript 中最嚴謹的邏輯。經過捶打磨練,成就了本書的中文版。本書包含了函數語言程式設計之精髓,希望可以幫助大家在學習函數語言程式設計的道路上走的更順暢。比心。
譯者團隊(排名不分先後):阿希、blueken、brucecham、cfanlife、dail、kyoko-df、l3ve、lilins、LittlePineapple、MatildaJin、冬青、pobusama、Cherry、蘿蔔、vavd317、vivaxy、萌萌、zhouyao
JavaScript 輕量級函數語言程式設計
附錄 C:函數語言程式設計函式庫
如果您已經從頭到尾通讀了此書,請花一分鐘的時間停下來回顧一下從第 1 章到現在的收穫。相當漫長的一段旅程,不是嗎?希望您已經收穫了大量新知識,並用函式式的方式思考你的程式。
在本書即將完結時,我想給你提供一些關於使用官方函數語言程式設計函式庫的快速指南。注意這並不是一個詳細的文件,而是將你在結束“輕量級函數語言程式設計”後進軍真正的函數語言程式設計時應該注意的東西快速梳理一下。
如果有可能,我建議你不要做重新造輪子這樣的事情。如果你找到了一個能滿足你需求的函數語言程式設計函式庫,那麼用它就對了。只有在你實在找不到合適的庫來應對你面臨的問題時,才應該使用本書提供的輔助實用函式 —— 或者自己造輪子。
目錄
在本書第 1 章曾列出了一個函數語言程式設計庫的列表,現在我們來擴充套件這個列表。我們不會涉及所有的庫(它們之中有許多重複的內容),但下面這些你應該有所關注:
- Ramda:通用函數語言程式設計實用函式
- Sanctuary:函數語言程式設計型別 Ramda 伴侶
- lodash/fp:通用函數語言程式設計實用函式
- functional.js:通用函數語言程式設計實用函式
- Immutable:不可變資料結構
- Mori:(受到 ClojureScript 啟發)不可變資料結構
- Seamless-Immutable:不可變資料助手
- tranducers-js:資料轉換器
- monet.js:Monad 型別
上面的列表只列出了所有函數語言程式設計庫的一小部分,並不是說沒有在列表中列出的庫就不好,也不是說列表中列出的就是最佳選擇,總之這只是 JavaScript 函數語言程式設計世界中的一瞥。您可以前往這裡檢視更完整的函數語言程式設計資源。
Fantasy Land(又名 FL)是函數語言程式設計世界中十分重要的學習資源之一,與其說它是一個庫,不如說它是一本百科全書。
Fantasy Land 不是一份為初學者準備的輕量級讀物,而是一個完整而詳細的 JavaScript 函數語言程式設計路線圖。為了儘可能提升互通性,FL 已經成為 JavaScript 函數語言程式設計庫遵循的實際標準。
Fantasy Land 與“輕量級函數語言程式設計”的概念相反,它以火力全開的姿態進軍 JavaScript 的函數語言程式設計世界。也就是說,當你的能力超越本書時,FL 將會成為你接下來前進的方向。我建議您將其儲存在收藏夾中,並在您使用本書的概念進行至少 6 個月的實戰練習之後再回來。
Ramda (0.23.0)
摘自 Ramda 文件:
Ramda 函式自動地被柯里化。
Ramda 函式的引數經過優化,更便於柯里化。需要被操作的資料往往放在最後提供。
我認為合理的設計是 Ramda 的優勢之一。值得注意的是,Ramda 的柯里化形式(似乎大多數的庫都是這種形式)是我們在第 3 章中討論過的“鬆散柯里化”。
第 3 章的最後一個例子 —— 我們定義無值(point-free)工具函式 printIf()
—— 可以在 Ramda 中這樣實現:
function output(msg) {
console.log( msg );
}
function isShortEnough(str) {
return str.length <= 5;
}
var isLongEnough = R.complement( isShortEnough );
var printIf = R.partial( R.flip( R.when ), [output] );
var msg1 = "Hello";
var msg2 = msg1 + " World";
printIf( isShortEnough, msg1 ); // Hello
printIf( isShortEnough, msg2 );
printIf( isLongEnough, msg1 );
printIf( isLongEnough, msg2 ); // Hello World
複製程式碼
與我們在第 3 章中的實現相比有幾處不同:
-
我們使用
R.complement(..)
而不是not(..)
在isShortEnough(..)
周圍新建一個否定函式isLongEnough(..)
。 -
使用
R.flip(..)
而不是reverseArgs(..)
函式,值得一提的是,R.flip(..)
僅交換頭兩個引數,而reverseArgs(..)
會將所有引數反向。在這種情景下,flip(..)
更加方便,所以我們不再需要使用partialRight(..)
或其他投機取巧的方式進行處理。 -
R.partial(..)
所有的後續引數以單個陣列的形式存在。 -
因為 Ramda 使用鬆散柯里化,因此我們不需要使用
R.uncurryN(..)
來獲得一個包含所有引數的printIf(..)
。如果我們這樣做了,就相當於使用R.uncurryN(2, ..)
包裹R.partial(..)
進行呼叫,這是完全沒有必要的。
Ramda 是一個受歡迎的、功能強大的庫。如果你想要在你的程式碼中實踐 FP,從 Ramda 開始是個不錯的選擇。
Lodash/fp (4.17.4)
Lodash 是整個 JS 生態系統中最受歡迎的庫。Lodash 團隊釋出了一個“FP 友好”的 API 版本 —— "lodash/fp"。
在第 8 章中,我們討論了合併獨立列表操作(map(..)
、filter(..)
以及 reduce(..)
)。使用“lodash/fp”時,你可以這樣做:
var sum = (x,y) => x + y;
var double = x => x * 2;
var isOdd = x => x % 2 == 1;
fp.compose( [
fp.reduce( sum )( 0 ),
fp.map( double ),
fp.filter( isOdd )
] )
( [1,2,3,4,5] ); // 18
複製程式碼
與我們所熟知的 _.
名稱空間字首不同,“lodash/fp”將 fp.
定義為其名稱空間字首。我發現一個很有用的區別,就是 fp.
比 _.
更容易識別。
注意 fp.compose(..)
(在常規 lodash 版本中又名 _.flowRight(..)
)接受一個函式陣列,而不是獨立的函式作為引數。
lodash 擁有良好的穩定性、廣泛的社群支援以及優秀的效能,是你探索 FP 世界時的堅實後盾。
Mori (0.3.2)
在第 6 章中,我們已經快速瀏覽了一下 Immutable.js 庫,該庫可能是最廣為人知的不可變資料結構庫了。
讓我們來看一下另一個流行的庫:Mori。Mori 設計了一套與眾不同(從表面上看更像函數語言程式設計)的 API:它使用獨立的函式而不直接在值上操作。
var state = mori.vector( 1, 2, 3, 4 );
var newState = mori.assoc(
mori.into( state, Array.from( {length: 39} ) ),
42,
"meaning of life"
);
state === newState; // false
mori.get( state, 2 ); // 3
mori.get( state, 42 ); // undefined
mori.get( newState, 2 ); // 3
mori.get( newState, 42 ); // "meaning of life"
mori.toJs( newState ).slice( 1, 3 ); // [2,3]
複製程式碼
這是一個指出關於 Mori 的一些有趣的事情的例子:
-
使用
vector
而不是list
(你可能會想用的),主要是因為文件說它的行為更像 JavaScript 中的陣列。 -
不能像在操作原生 JavaScript 陣列那樣在任意位置設定值,在 vector 結構中,這將會丟擲異常。因此我們必須使用
mori.into(..)
,傳入一個合適長度的陣列來擴充套件 vector 的長度。在上例中,vector 有 43 個可用位置(4 + 39),所以我們可以在最後一個位置(索引為 42)上寫入"meaning of life"
這個值。 -
使用
mori.into(..)
建立一個較大的 vector,再用mor.assoc(..)
根據這個 vector 建立另一個 vector 的做法聽起來效率低下。但是,不可變資料結構的好處在於資料不會進行克隆,每次“改變”發生,新的資料結構只會追蹤其與舊資料結構的不同之處。
Mori 受到 ClojureScript 極大的啟發。如果您有 ClojureScript 程式設計經驗,那您應該對 Mori 的 API 感到非常熟悉。由於我沒有這種程式設計經驗,因此我感覺 Mori 中的方法名有點奇怪。
但相比於在資料上直接呼叫方法,我真的很喜歡呼叫獨立方法這樣的設計。Mori 還有一些自動返回原生 JavaScript 陣列的方法,用起來非常方便。
總結
JavaScript 不是作為函數語言程式設計語言來特別設計的。不過其自身的確擁有很多對函數語言程式設計非常友好基礎語法(例如可作為變數的函式、閉包等)。本章提及的庫將使你更方便的進行函數語言程式設計。
有了本書中函數語言程式設計概念的武裝,相信你已經準備好開始處理現實世界的程式碼了。找一個優秀的函數語言程式設計庫來用,然後練習,練習,再練習。
就是這樣了。我已經將我目前所知道的知識分享給你了。我在此正式認證您為“JavaScript 輕量級函數語言程式設計”程式設計師!好了,是時候結束我們一起學習 FP 這部分的“章節”了,但我的學習之旅還將繼續。我希望,你也是!
**【上一章】翻譯連載 | 附錄 B: 謙虛的 Monad-《JavaScript輕量級函數語言程式設計》 |《你不知道的JS》姊妹篇 **
iKcamp原創新書《移動Web前端高效開發實戰》已在亞馬遜、京東、噹噹開售。
iKcamp官網:www.ikcamp.com 訪問官網更快閱讀全部免費分享課程: 《iKcamp出品|全網最新|微信小程式|基於最新版1.0開發者工具之初中級培訓教程分享》 《iKcamp出品|基於Koa2搭建Node.js實戰專案教程》 包含:文章、視訊、原始碼
iKcamp新課程推出啦~~~~~開始免費連載啦~每週2更共11堂iKcamp課|基於Koa2搭建Node.js實戰專案教學(含視訊)| 課程大綱介紹
2019年,iKcamp原創新書《Koa與Node.js開發實戰》已在京東、天貓、亞馬遜、噹噹開售啦!