對(duì)于函數(shù)式的版本,乍一看,的確令人非常費(fèi)解,仔細(xì)看一下,你可能就暈掉了,似乎完全就是天書,看上去非常裝逼,哈哈。不過,我感覺解析那段函數(shù)式的代碼可能會(huì)一個(gè)比較有趣過程。
先看代碼
這個(gè)代碼平淡無奇,就是從一個(gè)數(shù)組中找到一個(gè)數(shù),O(n)的算法,找不到就返回 null。
下面是正常的 old-school 的方式。不用多說。

結(jié)果到了函數(shù)式成了下面這個(gè)樣子(好像上面的那些代碼在下面若影若現(xiàn),不過又有點(diǎn)不太一樣,為了消掉if語言,讓其看上去更像一個(gè)表達(dá)式,動(dòng)用了 ? 號(hào)表達(dá)式):

為了講清這個(gè)代碼,需要先補(bǔ)充一些知識(shí)。
Javascript的箭頭函數(shù)
首先先簡(jiǎn)單說明一下,ECMAScript2015 引入的箭頭表達(dá)式。箭頭函數(shù)其實(shí)都是匿名函數(shù),其基本語法如下:

下面是一些示例:

看上去不復(fù)雜吧。不過,上面前兩個(gè) simple 和 max 的例子都把這箭頭函數(shù)賦值給了一個(gè)變量,于是它就有了一個(gè)名字。有時(shí)候,某些函數(shù)在聲明的時(shí)候就是調(diào)用的時(shí)候,尤其是函數(shù)式編程中,一個(gè)函數(shù)還對(duì)外返回函數(shù)的時(shí)候。比如下在這個(gè)例子:

其實(shí),在 MakePowerFn 函數(shù)里的那個(gè) PowerFn 根本不需要命名,完全可以寫成:

如果用箭頭函數(shù),可以寫成:

我們還可以寫得更簡(jiǎn)潔(如果用表達(dá)式的話,就不需要 { 和 }, 以及 return 語句 ):

我還是加上括號(hào),和換行可能會(huì)更清楚一些:

好了,有了上面的知識(shí),我們就可以進(jìn)入一個(gè)更高級(jí)的話題——匿名函數(shù)的遞歸。
匿名函數(shù)的遞歸
函數(shù)式編程立志于用函數(shù)表達(dá)式消除有狀態(tài)的函數(shù),以及for/while循環(huán),所以,在函數(shù)式編程的世界里是不應(yīng)該用for/while循環(huán)的,而要改用遞歸(遞歸的性能很差,所以,一般是用尾遞歸來做優(yōu)化,也就是把函數(shù)的計(jì)算的狀態(tài)當(dāng)成參數(shù)一層一層的往下傳遞,這樣語言的編譯器或解釋器就不需要用函數(shù)棧來幫你保存函數(shù)的內(nèi)部變量的狀態(tài)了)。
好了,那么,匿名函數(shù)的遞歸該怎么做?
一般來說,遞歸的代碼就是函數(shù)自己調(diào)用自己,比如我們求階乘的代碼:

在匿名函數(shù)下,這個(gè)遞歸該怎么寫呢?對(duì)于匿名函數(shù)來說,我們可以把匿名函數(shù)當(dāng)成一個(gè)參數(shù)傳給另外一個(gè)函數(shù),因?yàn)楹瘮?shù)的參數(shù)有名字,所以就可以調(diào)用自己了。?如下所示:

這個(gè)是不是有點(diǎn)作弊的嫌疑?Anyway,我們?cè)偻拢焉厦孢@個(gè)函數(shù)整成箭頭函數(shù)式的匿名函數(shù)的樣子。

現(xiàn)在你似乎就不像作弊了吧。把上面那個(gè)求階乘的函數(shù)套進(jìn)來是這個(gè)樣子:
首先,先重構(gòu)一下fact,把fact中自己調(diào)用自己的名字去掉:

然后,我們?cè)侔焉厦孢@個(gè)版本變成箭頭函數(shù)的匿名函數(shù)版:

這里,我們依然還要用一個(gè)fact來保存這個(gè)匿名函數(shù),我們繼續(xù),我們要讓匿名函數(shù)聲明的時(shí)候,就自己調(diào)用自己。
也就是說,我們要把?

這個(gè)函數(shù)當(dāng)成調(diào)用參數(shù),傳給下面這個(gè)函數(shù):

最終我們得到下面的代碼:

好像有點(diǎn)繞,anyway, 你看懂了嗎?沒事,我們繼續(xù)。
動(dòng)用高階函數(shù)的遞歸
但是上面這個(gè)遞歸的匿名函數(shù)在自己調(diào)用自己,所以,代碼中有hard code的實(shí)參。我們想實(shí)參去掉,如何去掉呢?我們可以參考前面說過的那個(gè)

我們可以看,上面的代碼簡(jiǎn)單說來就是,需要一個(gè)函數(shù)做參數(shù),然后返回這個(gè)函數(shù)的遞歸版本。那么,我們?cè)趺凑{(diào)用呢?

連起來寫就是:

但是,這樣讓用戶來調(diào)用很不爽,所以,以我們一個(gè)函數(shù)把?HighOrderFact ( HighOrderFact )?給代理一下:

用箭頭函數(shù)重構(gòu)一下,是不是簡(jiǎn)潔了一些?

上面就是我們最終版的階乘的函數(shù)式代碼。
回顧之前的程序
我們?cè)賮砜茨莻€(gè)查找數(shù)組的正常程序:

先把for干掉,搞成遞歸版本:

然后,寫出帶實(shí)參的匿名函數(shù)的版本(注:其中的if代碼被重構(gòu)成了 ?號(hào)表達(dá)式):

最后,引入高階函數(shù),去除實(shí)參:

注:函數(shù)式編程裝逼時(shí)一定要用const字符,這表示我寫的函數(shù)里的狀態(tài)是 immutable 的,天生驕傲!
再注:我寫的這個(gè)比原來版的那個(gè)簡(jiǎn)單了很多,原來版本的那個(gè)又在函數(shù)中套了一套 next, 而且還動(dòng)用了不定參數(shù),當(dāng)然,如果你想裝逼裝到天上的,理論上來說,你可以套N層,呵呵。
現(xiàn)在,你可以體會(huì)到,如此逼裝的是怎么來的了吧?

轉(zhuǎn)自:https://mp.weixin.qq.com/s/DYTAS5r5tFZyT-JotC4Pkg