<b>本文講的是高階函數(軟體編寫)(第四部分),</b>
Smoke Art Cubes to Smoke — MattysFlicks — (CC BY 2.0)
之前我們看到的 <code>.map()</code> 和 <code>.filter()</code> 都是高階函數 —— 它們都接受一個函數作為參數,
先來看個一階函數的例子,該函數會将單詞數組中 4 個字母的單詞過濾掉:
如果又要選擇出所有以 's' 開頭的單詞呢?可以再定義一個函數:
顯然可以看出這裡面有很多重複的代碼,這兩個函數的主體是相同的 —— 都是周遊一個數組并根據給定的條件進行過濾。這便形成了一種特定的模式,可以從中抽象出更為通用的解決方案。
不難看出, “周遊”和“過濾”都是亟待抽象出來的,以便分享和複用到其他所有類似的函數中去。畢竟,從數組中選取某些特定元素是很常見的需求。
幸運的是,函數是 JavaScript 中的一等公民,就像數字、字元串和對象一樣,函數可以:
像變量一樣指派給其他變量
作為對象的屬性值
作為參數進行傳遞
作為函數的傳回值
函數基本上可以像其他任何資料類型一樣被使用,這點使得“抽象”容易了許多。例如,可以定義一種函數,将周遊數組并累計出一個傳回值的過程抽象出來,該函數接收一個函數作為參數來決定具體的累計過程,不妨将此函數稱為 reducer:
該 <code>reduce()</code> 接受 3 個參數:一個 reducer 函數、一個累計的初始值和一個用于周遊的數組。對數組中的每個元素都會調用 reducer,傳入累計器和目前數組元素,傳回值又會賦給累計器。對數組中的所有元素都執行過 reducer 之後,傳回最終的累計結果。
在用例中,調用 <code>reduce</code> 并傳給它 3 個參數:<code>reducer</code> 函數、初始值 0 以及需要周遊的數組。其中 <code>reducer</code> 函數以累計器和目前數組元素為參數,傳回累計後的結果。
如此将周遊和累計的過程抽象出來之後,便可實作更為通用的 <code>filter()</code> 函數:
在此 <code>filter()</code> 函數中,除了以參數形式傳進來的 <code>fn()</code> 函數以外,所有代碼都是可複用的。其中 <code>fn()</code> 參數被稱為斷言(predicate) —— 傳回一個布爾值的函數。
将目前值傳給 <code>fn()</code>,如果 <code>fn(curr)</code> 傳回 <code>true</code>,則将 <code>curr</code> 添加到結果數組中并傳回之;否則,直接傳回目前數組。
現在便可借助 <code>filter()</code> 函數來實作過濾 4 字母單詞的 <code>censor()</code> 函數:
喔!将所有公共代碼抽象出來之後,<code>censor()</code> 函數便十分簡潔了。
<code>startsWithS()</code> 也是如此:
你若稍加留意便會發現 JavaScript 其實已經為我們做了這些抽象,即 <code>Array.prototype</code> 的相關方法,例如 <code>.reduce()</code>、<code>.filter()</code>、<code>.map()</code> 等等。
高階函數也常常被用于對不同資料類型的操作進行抽象。例如,<code>.filter()</code> 函數不一定非得作用于字元串數組。隻需傳入一個能夠處理不同資料類型的函數,<code>.filter()</code> 便能過濾數字了。還記得 <code>highpass</code> 的例子嗎?
換言之,高階函數可以用來實作函數的多态性。如你所見,相對于一階函數而言,高階函數的複用性和通用性更好。一般來講,在實際編碼中會組合使用高階函數和一些非常簡單的一階函數。
<b></b>
<b>原文釋出時間為:2017年4月19日</b>
<b>本文來自雲栖社群合作夥伴掘金,了解相關資訊可以關注掘金網站。</b>