摘要:結論這次主要介紹了函數式編程中的函數的原理和實現方法,由于篇幅原因,我把打算分析的源碼實現放到下一篇來介紹,可以說實現的更加函數式,需要多帶帶好好分析。
上一篇文章介紹了javascript函數式編程中curry(柯里化)的實現,當然那個柯里化是有限參數的柯里化,等有機會在補上無限參數的那一種柯里化,這次主要說的是javascript函數式編程中另外一個很重要的函數compose,compose函數的作用就是組合函數的,將函數串聯起來執行,將多個函數組合起來,一個函數的輸出結果是另一個函數的輸入參數,一旦第一個函數開始執行,就會像多米諾骨牌一樣推導執行了。
簡介比如有這樣的需求,要輸入一個名字,這個名字有由firstName,lastName組合而成,然后把這個名字全部變成大寫輸出來,比如輸入jack,smith我們就要打印出來,‘HELLO,JACK SMITH’ 。
我們考慮用函數組合的方法來解決這個問題,需要兩個函數greeting, toUpper
var greeting = (firstName, lastName) => "hello, " + firstName + " " + lastName var toUpper = str => str.toUpperCase() var fn = compose(toUpper, greeting) console.log(fn("jack", "smith")) // ‘HELLO,JACK SMITH’
這就是compose大致的使用,總結下來要注意的有以下幾點
compose的參數是函數,返回的也是一個函數
因為除了第一個函數的接受參數,其他函數的接受參數都是上一個函數的返回值,所以初始函數的參數是多元的,而其他函數的接受值是一元的
compsoe函數可以接受任意的參數,所有的參數都是函數,且執行方向是自右向左的,初始函數一定放到參數的最右面
知道這三點后,就很容易的分析出上個例子的執行過程了,執行fn("jack", "smith")的時候,初始函數為greeting,執行結果作為參數傳遞給toUpper,再執行toUpper,得出最后的結果,compose的好處我簡單提一下,如果還想再加一個處理函數,不需要修改fn,只需要在執行一個compose,比如我們再想加一個trim,只需要這樣做
var trim = str => str.trim() var newFn = compose(trim, fn) console.log(newFn("jack", "smith"))
就可以了,可以看出不論維護和擴展都十分的方便。
實現例子分析完了,本著究其根本的原則,還是要探究與一下compose到底是如何實現的,首先解釋介紹一下我是如何實現的,然后再探求一下,javascript函數式編程的兩大類庫,lodash.js和ramda.js是如何實現的,其中ramda.js實現的過程非常函數式。
我的實現我的思路是,既然函數像多米諾骨牌式的執行,我首先就想到了遞歸,下面就一步一步的實現這個compose,首先,compose返回一個函數,為了記錄遞歸的執行情況,還要記錄參數的長度len,還要給返回的函數添加一個名字f1。
var compose = function(...args) { var len = args.length return function f1() { } }
函數體里面要做的事情就是不斷的執行args中的函數,將上一個函數的執行結果作為下一個執行函數的輸入參數,需要一個游標count來記錄args函數列表的執行情況。
var compose = function(...args) { var len = args.length var count = len - 1 var result return function f1(...args1) { result = args[count].apply(this, args1) count-- return f1.call(null, result) } }
這個就是思路,當然這樣是不行的,沒有退出條件,遞歸的退出條件就是最后一個函數執行完的時候,也就是count為0的時候,這時候,有一點要注意,遞歸退出的時候,count游標一定要回歸初始狀態,最后補充一下代碼
var compose = function(...args) { var len = args.length var count = len - 1 var result return function f1(...args1) { result = args[count].apply(this, args1) if (count <= 0) { count = len - 1 return result } else { count-- return f1.call(null, result) } } }
這樣就實現了這個compose函數。后來我發現遞歸這個完全可以使用迭代來實現,使用while函數看起來更容易明白,其實lodash.js就是這么實現的。
lodash實現lodash的思路同上,不過是用迭代實現的,我就把它的源代碼貼過來看一下
var flow = function(funcs) { var length = funcs.length var index = length while (index--) { if (typeof funcs[index] !== "function") { throw new TypeError("Expected a function"); } } return function(...args) { var index = 0 var result = length ? funcs[index].apply(this, args) : args[0] while (++index < length) { result = funcs[index].call(this, result) } return result } } var flowRight = function(funcs) { return flow(funcs.reverse()) }
可以看出,lodash的本來實現是從左到右的,但也提供了從右到左的flowRight,還多了一層函數的校驗,而且接收的是數組,不是參數序列,而且從這行var result = length ? funcs[index].apply(this, args) : args[0]可以看出允許數組為空,可以看出還是非常嚴謹的。我寫的就缺少這種嚴謹的異常處理。
結論這次主要介紹了函數式編程中的compose函數的原理和實現方法,由于篇幅原因,我把打算分析的ramda.js源碼實現放到下一篇來介紹,可以說ramda.js實現的compose更加函數式,需要多帶帶好好分析。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/81571.html
摘要:把數據的流向想象成糖果工廠的一條傳送帶,每一次操作其實都是冷卻切割包裝糖果中的一步。在該章節中,我們將會用糖果工廠的類比來解釋什么是組合。糖果工廠靠這套流程運營的很成功,但是和所有的商業公司一樣,管理者們需要不停的尋找增長點。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 關于譯者:這是一個流淌...
摘要:注意是單一參數柯里化是由以邏輯學家命名的,當然編程語言也是源自他的名字,雖然柯里化是由和發明的。辨別類型和它們的含義是一項重要的技能,這項技能可以讓你在函數式編程的路上走得更遠。 slide 地址 三、可以,這很函數式~ showImg(https://segmentfault.com/img/remote/1460000015978685?w=187&h=160); 3.1.函數是一...
摘要:函數式編程逐漸被邊緣化,被拋棄到學術界和非主流的場外。組合式編程的重新崛起年左右,有個巨大的變化爆發了。人們開始逐漸在私下里談論函數式編程。箭頭函數對于函數式編程的爆發起到了推動劑的作用。現在很少看到那種不用函數式編程的大型應用了。 showImg(https://segmentfault.com/img/remote/1460000009036867?w=800&h=364); 本...
摘要:與條件判斷一般我們判斷或用如果我們有更多水果與去重提供了新的數據結構。所以所有的關于數據的操作,都可以用函數式的方式處理。這樣做的可讀性遠遠高于嵌套一大堆的函數調用我們選擇一些函數,讓它們結合,生成一個嶄新的函數。 1、Array.includes 與條件判斷 一般我們判斷或用 || // condition function test(fruit) { if (fruit...
閱讀 1642·2023-04-25 18:27
閱讀 1393·2021-10-19 11:44
閱讀 570·2021-10-14 09:42
閱讀 2144·2021-10-11 10:59
閱讀 2777·2021-09-24 09:47
閱讀 1728·2019-08-30 14:20
閱讀 1158·2019-08-30 14:08
閱讀 735·2019-08-29 15:15