今天要提到的是讓 JS 很適合用來撰寫 Functional Programming 的兩個特性的名詞解釋:「 一級函式」與「高階函式」,如果你寫 JS 一段時間,一定會聽過他,高階函式與一級函式可能聽起來有點複雜,其實並不會,只是字面上意思比較不好理解而已。這兩個特性,讓 JS 可以把函式在其他函式之間互傳,所以也是為什麼有人說 JS 很適合用來寫 Functional Programming 的原因。
Outline
- 一級函式
- 高階函式
一級函式 ( First-class functions )
當我們說一個語言具有一級函式的特性時,代表這個語言把函式當作其他物件一樣看待,也因此可以將函式當作參數一樣傳入另外一個函式裡面。在 Functional Programming 裡面,也是因為這個特性,才有辦法做到複合函式 (Function Composition),
而在 JS 內,函式本身也是一個特殊的物件(就是 Function 物件),在一些使用到 callback 概念的程式碼中,你就會看到這個概念是如何被應用的:
function doSomething(fn, data) {
return fn(data);
}
我們可以試試下面的程式碼來確認上面的描述 :
function hello (){
console.log('hello')
}
hello.a = 'a'
console.log(hello.a) //'a'
雖然上面的程式碼完全是合法的,因為函式本來就也是物件,但是在實務上請不要這麼做,否則同事或是跟你一起合作的人可能會崩潰,請使用一般的物件。
而既然將函式當作物件一樣看待,那就代表也可以把這個函式指派給變數,這就是我們之前提到的「函式表達式」 ( Function Expression ) 。
let hello = function (){
//do some thing
}
高階函式 ( High Order Function )
只要是可以接收函式作為參數,或是回傳函式作為輸出的函式,我們就稱為高階函式,例如,JS 裡面很常用的一些對陣列操作的內建API:
- Array.map( ()⇒{…} )
- Array.filter( ()⇒{…} )
- Array.reduce( ()⇒{…} )
也可以被稱為是高階函式,因為他們能夠接收函式作為他們的參數。雖然上述幾個 API 的使用方式乍看之下可能會讓人覺得難以理解,但我們可以試著思考看看他們是怎麼被實作的,其實並沒有那麼複雜,下面就以 Array.map 為例,邊實作、邊思考他的運作方式吧!
由於 Arrray.map 是對陣列元素做巡訪,然後做某些操作之後回傳,所以可能的步驟如下:
- 將函式傳入 map 內
- 執行一個以陣列長度為執行次數的迴圈
- 每次帶入不同的 array id 以表示目前尋訪的進度
- 取得陣列元素、逐個進行修改
- 逐個放入新的陣列並回傳
自己實際實作 map function 的話看起來會像是這樣:
function arrayMap(fn,array){
let length = array.length
let newArray = []
for(let i=0 ; i<length ; i++){
newArray.push(fn(array[i]))
}
return newArray
}
透過上面的程式碼我們自己就實作了高階函式 arrayMap ,可以看到我們自己做的 arrayMap 會在陣列傳入之後,逐個訪問每個元素並傳入我們自己寫的函式 fn ,這個 fn 會根據我們寫的內容將該值做處理之後回傳,然後會直接透過 Array.push 將結果推入新的函式( 看到了嗎?這裡我們用到複合函式的概念 )
arrayMap((item)=>{
return item * 2
},[1,2,3,4])
結論
透過今天對兩個名詞的說明我們知道了一級函式與高階函式這兩個名詞的意義,然後我們也自己試著實作了自己的高階函式:
- 一級函式是指在一個語言內,函式本身也是物件,因此能夠將函式當成參數傳給另一函式
- 高階函式則是指一個函式能不能接收函式當作參數,或是回傳函式作為回傳值