高階函數英文叫Higher-order function。那麼什麼是高階函數?
JavaScript的函數其實都指向某個變量。既然變量可以指向函數,函數的參數能接收變量,那麼一個函數就可以接收另一個函數作為參數,這種函數就稱之為高階函數。
一個最簡單的高階函數:
當我們調用<code>add(-5, 6, Math.abs)</code>時,參數<code>x</code>,<code>y</code>和<code>f</code>分别接收<code>-5</code>,<code>6</code>和函數<code>Math.abs</code>,根據函數定義,我們可以推導計算過程為:
用代碼驗證一下:
舉例說明,比如我們有一個函數<code>f(x)=x²</code>,要把這個函數作用在一個數組<code>[1, 2, 3, 4, 5, 6, 7, 8, 9]</code>上,就可以用<code>map</code>實作如下:

由于<code>map()</code>方法定義在JavaScript的<code>Array</code>中,我們調用<code>Array</code>的<code>map()</code>方法,傳入我們自己的函數,就得到了一個新的<code>Array</code>作為結果:
<font color="red">注意</font>: <code>map()</code>傳入的參數是<code>pow</code>,即函數對象本身。
有人可能會想,不需要<code>map()</code>,寫一個循環,也可以計算出結果:
的确可以,但是,從上面的循環代碼,我們無法一眼看明白把<code>f(x)</code>作用在<code>Array</code>的每一個元素并把結果生成一個新的<code>Array</code>”。
是以,<code>map()</code>作為高階函數,事實上它把運算規則抽象了,是以,我們不但可以計算簡單的<code>f(x)=x²</code>,還可以計算任意複雜的函數,比如,把<code>Array</code>的所有數字轉為字元串:
隻需要一行代碼。
再看<code>reduce</code>的用法。<code>Array</code>的<code>reduce()</code>把一個函數作用在這個<code>Array</code>的<code>[x1, x2, x3...]</code>上,這個函數必須接收兩個參數,<code>reduce()</code>把結果繼續和序列的下一個元素做累積計算,其效果就是:
<code>[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)</code>
比方說對一個<code>Array</code>求和,就可以用<code>reduce</code>實作:
練習:利用<code>reduce()</code>求積:
要把<code>[1, 3, 5, 7, 9]</code>變換成整數<code>13579</code>,<code>reduce()</code>也能派上用場:
如果我們繼續改進這個例子,想辦法把一個字元串<code>13579</code>先變成<code>Array</code>——<code>[1, 3, 5, 7, 9]</code>,再利用<code>reduce()</code>就可以寫出一個把字元串轉換為Number的函數。
練習:
不要使用JavaScript内置的parseInt()函數,利用map和reduce操作實作一個string2int()函數:
請把使用者輸入的不規範的英文名字,變為首字母大寫,其他小寫的規範名字。輸入:<code>['adam', 'LISA', 'barT']</code>,輸出:<code>['Adam', 'Lisa', 'Bart']</code>。
小明希望利用map()把字元串變成整數,他寫的代碼很簡潔:
結果竟然是<code>1, NaN, NaN</code>,小明百思不得其解,請幫他找到原因并修正代碼。
<code>filter</code>也是一個常用的操作,它用于把<code>Array</code>的某些元素過濾掉,然後傳回剩下的元素。
和<code>map()</code>類似,<code>Array</code>的<code>filter()</code>也接收一個函數。和<code>map()</code>不同的是,<code>filter()</code>把傳入的函數依次作用于每個元素,然後根據傳回值是<code>true</code>還是<code>false</code>決定保留還是丢棄該元素。
例如,在一個<code>Array</code>中,删掉偶數,隻保留奇數,可以這麼寫:
把一個Array中的空字元串删掉,可以這麼寫:
可見用<code>filter()</code>這個高階函數,關鍵在于正确實作一個“篩選”函數。
回調函數
<code>filter()</code>接收的回調函數,其實可以有多個參數。通常我們僅使用第一個參數,表示<code>Array</code>的某個元素。回調函數還可以接收另外兩個參數,表示元素的位置和數組本身:
利用<code>filter</code>,可以巧妙地去除<code>Array</code>的重複元素:
去除重複元素依靠的是<code>indexOf</code>總是傳回第一個元素的位置,後續的重複元素位置與<code>indexOf</code>傳回的位置不相等,是以被<code>filter</code>濾掉了。
練習
請嘗試用<code>filter()</code>篩選出素數:
排序也是在程式中經常用到的算法。無論使用冒泡排序還是快速排序,排序的核心是比較兩個元素的大小。如果是數字,我們可以直接比較,但如果是字元串或者兩個對象呢?直接比較數學上的大小是沒有意義的,是以,比較的過程必須通過函數抽象出來。通正常定,對于兩個元素x和y,如果認為<code>x &lt; y</code>,則傳回<code>-1</code>,如果認為<code>x == y</code>,則傳回<code>0</code>,如果認為<code>x &gt; y</code>,則傳回<code>1</code>,這樣,排序算法就不用關心具體的比較過程,而是根據比較結果直接排序。
JavaScript的<code>Array</code>的<code>sort()</code>方法就是用于排序的,但是排序結果可能讓你大吃一驚:
第二個排序把<code>apple</code>排在了最後,是因為字元串根據ASCII碼進行排序,而小寫字母<code>a</code>的ASCII碼在大寫字母之後。
第三個排序結果是為什麼呢?簡單的數字排序都能錯?
這是因為<code>Array</code>的<code>sort()</code>方法預設把所有元素先轉換為<code>String</code>再排序,結果<code>'10'</code>排在了<code>'2'</code>的前面,因為字元<code>'1'</code>比字元<code>'2'</code>的ASCII碼小。
如果不知道<code>sort()</code>方法的預設排序規則,直接對數字排序,絕對栽進坑裡!
幸運的是,<code>sort()</code>方法也是一個高階函數,它還可以接收一個比較函數來實作自定義的排序。
要按數字大小排序,我們可以這麼寫:
如果要倒序排序,我們可以把大的數放前面:
預設情況下,對字元串排序,是按照ASCII的大小比較的,現在,我們提出排序應該忽略大小寫,按照字母序排序。要實作這個算法,不必對現有代碼大加改動,隻要我們能定義出忽略大小寫的比較算法就可以:
忽略大小寫來比較兩個字元串,實際上就是先把字元串都變成大寫(或者都變成小寫),再比較。
從上述例子可以看出,高階函數的抽象能力是非常強大的,而且,核心代碼可以保持得非常簡潔。
最後友情提示,<code>sort()</code>方法會直接對<code>Array</code>進行修改,它傳回的結果仍是目前<code>Array</code>:
本文轉自 ygqygq2 51CTO部落格,原文連結:http://blog.51cto.com/ygqygq2/2071453,如需轉載請自行聯系原作者