把 function 當作參數

一個會讓人想很久的觀念。

重新認識參數

這種把 function 當作參數的用法最常用在 callback function,也可以翻作「回調函式」。

我覺得 callback function 可以當作是轉職任務的關卡吧,如果你能理解這個概念的話,就像是從盜賊轉職成刺客了的概念(到底在說什麼 XD)

但在學 callback function 之前得先弄懂這個概念,所以一步一步來就好,從我們最熟悉的情況開始:

1
2
3
4
function print(anything) {
console.log(anything)
}
print(123)

乍看之下沒有什麼,print(123) 執行後就會印出 123。

現在讓我們在加一個新的 function,然後試著把它當作參數(anything)傳進去:

1
2
3
4
5
6
7
function print(anything) {
console.log(anything)
}
function hello() {
console.log('hello')
}
print(hello)

之後我們會得到 [Function: hello] 這個結果,其實仔細想想的話也很合理,因為我們就是丟一個 function 進去,所以它 log 出來當然是跟我說「anything 的值是 hello 這個 function」

那我們如果想要執行 hello 這個 function 的話呢?

其實也沒有什麼困難的,既然現在 anything 代表的就是 hello,那我們就用 anything() 來執行就好了呀!

1
2
3
4
5
6
7
function print(anything) {
anything()
}
function hello() {
console.log('hello')
}
print(hello) // hello

把 function 當作參數的最基本的概念就是這樣而已,這個一定要理解。

稍微複雜一點的例子

如果要你實作下面這個 function,你會怎麼做?

1
function doSomething([1, 2, 3]) => [2, 4, 6]

我自己覺得比較直覺的作法是這樣:

1
2
3
4
5
6
7
8
9
10
11
function doSometing(array) {
var result = []
for (var i=0; i<array.length; i++) {
result.push(double(array[i]))
}
return result
}
function double(num) {
return num * 2
}
console.log(doSometing([1, 2, 3])) // [2, 4, 6]

但如果現在題目變成這樣呢?

1
function doSomething([1, 2, 3], Afunction) => [2, 4, 6]

按照前面所學,現在會多一個 Afunction 參數,所以得傳入一個 function 進去:

1
2
3
4
5
6
7
8
9
10
11
function doSometing(array, Afunction) {
var result = []
for (var i=0; i<array.length; i++) {
result.push(Afunction(array[i]))
}
return result
}
function double(num) {
return num * 2
}
console.log(doSometing([1, 2, 3], double)) // [2, 4, 6]

不知道你有沒有發現,這跟第一題根本差不多嘛,只是一個是在 doSometing 裡面去呼叫外面的 double ,一個是直接把 double 傳進去 doSometing

所以這兩個有什麼差嗎?不都解的出來?

別急,想想看如果現在題目又變了,變成這樣:

1
function doSomething([1, 2, 3], Afunction) => [3, 6, 9]

如果是用第一種作法的話,應該會做這樣的調整:

1
2
3
4
5
6
7
8
9
10
11
function doSometing(array) {
var result = []
for (var i=0; i<array.length; i++) {
result.push(triple(array[i]))
}
return result
}
function triple(num) {
return num * 3
}
console.log(doSometing([1, 2, 3])) // [2, 4, 6]
  1. 為了符合語意,你會先把 double 改名成 triple
  2. 修改 triple 中的內容

那如果是第二種做法呢?

1
2
3
4
5
6
7
8
9
10
11
function doSometing(array, Afunction) {
var result = []
for (var i=0; i<array.length; i++) {
result.push(Afunction(array[i]))
}
return result
}
function triple(num) {
return num * 3
}
console.log(doSometing([1, 2, 3], triple)) // [2, 4, 6]

嗯…好像也差不多?但在你扁我之前,請你先回憶一下「函式的宣告方式」,想一下下面的程式碼是不是也合理:

1
2
3
4
5
6
7
8
9
10
11
12
function doSometing(array, Afunction) {
var result = []
for (var i=0; i<array.length; i++) {
result.push(Afunction(array[i]))
}
return result
}
var triple = function (num) {
return num * 3
}
console.log(doSometing([1, 2, 3], triple)) // [2, 4, 6]

那如果我再改成這樣呢?

1
2
3
4
5
6
7
8
9
10
function doSometing(array, Afunction) {
var result = []
for (var i=0; i<array.length; i++) {
result.push(Afunction(array[i]))
}
return result
}
console.log(doSometing([1, 2, 3], function (num) {
return num * 3
})) // [2, 4, 6]

哦哦哦哦!你發現了嗎?當我們把函式當作參數傳入時,除了傳入一個「命名函式」之外,我們也可以傳入一個「匿名函式」來做處理。也就是說,不管我們想讓這個函式做什麼,我們都不用擔心語意的問題!

為什麼需要把 function 當作參數?

其實前面的例子,就算不把 function 當作參數也能解出來,所以可能體會不到為什麼需要這樣的做法。

但我覺得把 function 當作參數的一個想法是:你還不確定這個 function 要做什麼

以前面的例子來說,可以想成是這樣:

在我們實作 doSometing 時,可能會想要用一個 function 來做一段處理,所以就用 Afunction 來表示。只是我們實際要用它來做什麼還不知道,唯一知道的是 Afunction 會接受一個參數 Afunctuin(array[i])。但不論這個 Afunctuin 要實作的內容是什麼,都跟 doSometing 無關。

至於最後在傳入的時候該用「命名函式」還是「匿名函式」比較好?其實沒有正確的答案,這要看你考慮的是什麼。

像命名函式的好處是可讀性比較好,匿名函式的好處是彈性空間比較大,兩者各有優缺點。

函式的宣告方式 理解 function 傳遞參數的機制
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×