一個會讓人想很久的觀念。
重新認識參數
這種把 function 當作參數的用法最常用在 callback function,也可以翻作「回調函式」。
我覺得 callback function 可以當作是轉職任務的關卡吧,如果你能理解這個概念的話,就像是從盜賊轉職成刺客了的概念(到底在說什麼 XD)
但在學 callback function 之前得先弄懂這個概念,所以一步一步來就好,從我們最熟悉的情況開始:
1 | function print(anything) { |
乍看之下沒有什麼,print(123)
執行後就會印出 123。
現在讓我們在加一個新的 function,然後試著把它當作參數(anything)傳進去:
1 | function print(anything) { |
之後我們會得到 [Function: hello]
這個結果,其實仔細想想的話也很合理,因為我們就是丟一個 function 進去,所以它 log 出來當然是跟我說「anything
的值是 hello
這個 function」
那我們如果想要執行 hello
這個 function 的話呢?
其實也沒有什麼困難的,既然現在 anything
代表的就是 hello
,那我們就用 anything()
來執行就好了呀!
1 | function print(anything) { |
把 function 當作參數的最基本的概念就是這樣而已,這個一定要理解。
稍微複雜一點的例子
如果要你實作下面這個 function,你會怎麼做?
1 | function doSomething([1, 2, 3]) => [2, 4, 6] |
我自己覺得比較直覺的作法是這樣:
1 | function doSometing(array) { |
但如果現在題目變成這樣呢?
1 | function doSomething([1, 2, 3], Afunction) => [2, 4, 6] |
按照前面所學,現在會多一個 Afunction
參數,所以得傳入一個 function 進去:
1 | function doSometing(array, Afunction) { |
不知道你有沒有發現,這跟第一題根本差不多嘛,只是一個是在 doSometing
裡面去呼叫外面的 double
,一個是直接把 double
傳進去 doSometing
。
所以這兩個有什麼差嗎?不都解的出來?
別急,想想看如果現在題目又變了,變成這樣:
1 | function doSomething([1, 2, 3], Afunction) => [3, 6, 9] |
如果是用第一種作法的話,應該會做這樣的調整:
1 | function doSometing(array) { |
- 為了符合語意,你會先把
double
改名成triple
- 修改
triple
中的內容
那如果是第二種做法呢?
1 | function doSometing(array, Afunction) { |
嗯…好像也差不多?但在你扁我之前,請你先回憶一下「函式的宣告方式」,想一下下面的程式碼是不是也合理:
1 | function doSometing(array, Afunction) { |
那如果我再改成這樣呢?
1 | function doSometing(array, Afunction) { |
哦哦哦哦!你發現了嗎?當我們把函式當作參數傳入時,除了傳入一個「命名函式」之外,我們也可以傳入一個「匿名函式」來做處理。也就是說,不管我們想讓這個函式做什麼,我們都不用擔心語意的問題!
為什麼需要把 function 當作參數?
其實前面的例子,就算不把 function 當作參數也能解出來,所以可能體會不到為什麼需要這樣的做法。
但我覺得把 function 當作參數的一個想法是:你還不確定這個 function 要做什麼。
以前面的例子來說,可以想成是這樣:
在我們實作 doSometing
時,可能會想要用一個 function 來做一段處理,所以就用 Afunction
來表示。只是我們實際要用它來做什麼還不知道,唯一知道的是 Afunction
會接受一個參數 Afunctuin(array[i])
。但不論這個 Afunctuin
要實作的內容是什麼,都跟 doSometing
無關。
至於最後在傳入的時候該用「命名函式」還是「匿名函式」比較好?其實沒有正確的答案,這要看你考慮的是什麼。
像命名函式的好處是可讀性比較好,匿名函式的好處是彈性空間比較大,兩者各有優缺點。