【新手教程】JavaScript的柯里化函式

馬豔瓊發表於2015-11-14

柯里化,或者說部分應用,是一種函數語言程式設計的技術,對於熟悉以傳統方式編寫 JavaScript 程式碼的人來說可能會很費解。但如果使用得當,它可以使你的 JavaScript 函式更具可讀性。

更具可讀性和靈活性

函式式 JavaScript 被吹捧的優點之一就是擁有短小緊湊的程式碼風格,可以用最少行數、更少重複的程式碼得到正確的結果。有時這會以犧牲可讀性為代價;如果你還不熟悉函數語言程式設計的方法,這種方法寫的程式碼會很難閱讀和理解。

如果之前你遇到過柯里化這個術語,但是不知道它是什麼意思,把它當做奇怪的、難理解的技術而不去理會,這也是可以理解的。但是事實上柯里化是一個非常簡單的概念,它在處理函式引數的時候,涉及到一些常見的問題,同時為開發人員提供了靈活的選擇範圍。

什麼是柯里化

簡單來說,柯里化是一種允許使用部分函式引數建構函式的方式。也就是意味著,你在呼叫一個函式時,可以傳入需要的全部引數並獲得返回結果,也可以傳入部分引數並的得到一個返回的函式,它需要傳入的就是其餘的引數。它真的就是那麼簡單。

在像 Haskell 和 Scala 這樣的函數語言程式設計語言中,柯里化是其基本原理。 JavaScript 具有函數語言程式設計的能力,但是並沒有預設支援柯里化函式(至少目前的版本沒有支援)。但是我們已經知道了一些函式式的技巧,那我們就也可以讓在 JavaScript 裡實現柯里化。

為了讓你理解柯里化的原理,我們開始來寫第一個柯里化的 JavaScript 函式,使用熟悉的語法去新建一個我們想要的柯里化函式。比如說,我們假設有個函式是問候某個人的名字。我都很容易想到,建立一個簡單的函式,接受問候語和一個名字作為引數,然後在控制檯列印出整個問候的句子:

這個函式需要把名字和問候語兩個引數全部提供,才能夠正確執行。但是我們可以使用簡單的巢狀的柯里化方法重寫這個函式,這樣基本的函式只需要一個引數問候語,並返回以需要問候的名字為引數的另一個方法。

第一個柯里化函式

我們剛剛所寫的這種小小的調整,使我們獲得了一個可以接受任何問候語為引數的新函式,並且返回一個以我們想要問候的人名為引數新函式:

我們也可以直接呼叫原始的柯里化函式,只需要把每個引數分別加上小括號,一個接著一個:

為什麼不在你的瀏覽器裡試試?

全部都柯里化

炫酷的事情來了,現在我們已經學會了如何用這種方法處理傳統函式的引數,我們可以處理儘可能多的引數:

當有四個引數時和兩個引數相比,具有同樣地靈活性。無論巢狀多少層,我們都能夠寫出一個新的定製化的問候函式,無論我們選擇了多少人、以多少種想要的方式。

更重要的是,在原始的柯里化函式的基礎上,我們可以傳入需要的引數來建立一個新的變異函式,這個新的函式能夠繼續接受其餘的引數,每個引數分別在一個圓括號裡:

並且我們可以很容易地定義再下一級的變異函式:

柯里化經典函式

你可以看到這種方法是多麼的強大,尤其是當你需要許多複雜的定製化的函式時。唯一的問題就是語法。當你建立這些柯里化函式的時候,需要保證巢狀的返回子函式,並且呼叫它們的時候需要多組圓括號,每個都包含一個自己獨立的引數。這會變得一團糟。

為了解決這個問題,一種方法就是去新建一個快速的髒柯里化函式,它以一個已經存在的沒有巢狀返回的函式為引數。這個柯里化函式需要得到傳入函式的引數列表,並用這些函式返回原始函式的柯里化版本:

為了使用這種方式,我們傳入帶有任意個引數的函式名字,以及我們想預填充的若干個引數。我們得到的就是一個需要其餘引數的函式:

正如之前,我們在用柯里化函式構建子函式的時候,並不會限制引數的個數:

認真思考柯里化

我們這個小小的柯里化函式可能無法處理所有的邊緣情況,比如丟失或可選引數,但只要我們嚴格按照語法傳遞引數,它就能夠有效工作。

一些函式式的 JavaScript 庫,如 Ramda 擁有更靈活的柯里化功能,它可以打亂一個函式需要的引數,並允許你單獨或分組地傳入引數,以建立一個定製化的柯里化的變形函式。如果你想廣泛地柯里化,這可能是一個方向。

無論你選擇如何對程式進行柯里化,是隻用巢狀的括號還是更傾向於包括一個更穩健的柯里化函式,使用統一的命名規範有助於使你的程式碼更具可讀性。每個派生出的函式都應該有一個可以清楚表明它行為和期望引數的名字。

引數順序

進行柯里化的時候,一定要記住引數的順序很重要。使用我們剛剛講的方法,你很明顯想要原始函式的最後一個的引數,是從柯里化函式一層層變形得到的函式的引數。

提前考慮的引數順序會使柯里化更容易地應用到你的專案中。並且為了適應更多地情況,在設計函式時,考慮按照容易變化的程度排列引數的順序不是一個壞習慣。

結論

柯里化對於函式式 JavaScript 是一種極其有用的技術。它允許你生成一個簡潔、易配置、表現統一的庫,而且使用起來上手快、具有可讀性。在你的編碼實踐中加入柯里化會激勵你在全部程式碼中的部分函式上應用它,這樣避免了很多潛在的重複工作,並可以幫助你養成關於函式命名和處理函式引數的好習慣。

如果你喜歡這篇文章,你可能也會喜歡這個系列的其他文章:

相關文章