上一篇文章介紹了函數,本篇繼續介紹進階的函數用法。
在 Dart 裡,全部的項目都是 Object ,當然這也包括函數;這代表什麼意思呢?
List
的 forEach
void printIt(String element){
print(element);
}
var names = ['Alex', 'Johnson', 'Lex'];
list.forEach(printIt);
為什麼 list.forEach
可以接收 printIt
呢?
我們來看一下 forEach
的原始碼:
void forEach(void f(E element)) {
for (E element in this) f(element);
}
forEach
含有一個引數,而這個引數是 void f(E element)
,是不是有點眼熟呢?
這是一個函數的定義,函數名稱為 f
,回傳值為 void
,傳入的參數為 E element
,這邊的E表示是泛型,意思是任何型態都可以接受。(我將在後面介紹泛型)。
接者看看 forEach
的實作
for (E element in this) f(element);
這是一個迭代的 for
迴圈,其中 this
代表呼叫 forEach
的 list
,該迴圈會將 list
裡的每一個元素走訪一次,接者呼叫傳入 forEach
的引數,並將該元素傳給傳入的引數。
所以,當我們傳入一個 void printIt(String element)
,list
裡面的每一個元素都被丟進 printIt
,並且執行它。
那麼,如果要呼叫 forEach
函數,是不是一定要先定義一個函數,並將該函數傳給 forEach
呢?
→ 不用,我們可以使用匿名函數。
將上方的範例改為用匿名函數:
var names = ['Alex', 'Johnson', 'Lex'];
list.forEach((element){
print(element);
});
若一個函數可以接受函數作為引數,那麼我們不一定需要將需傳入的函數定義出來。我們可以直接在呼叫函數的地方直接實作它。
有時候,有些函數只有提供給物件使用,定義一個函數卻無法複用,此時就很適合使用匿名函數。
匿名函數如同一般的函數一樣,我們也可以使用表達式 (Expression) 表示。
將上方範例改為用表達式:
list.forEach((element) => print(element));
是不是簡單多了呢?
函數除了可以由函數作為引數,還可以當作回傳值
下面的範例:
Function timesX(int multiplicand){
return (int i) => i * multiplicand;
}
var time3 = timesX(3);
var time5 = timesX(5);
print(time3(2)); //6
print(time5(3)); //15
timesX(int multiplicand)
:將 multiplicand 傳入函數 timesX
中,並與函數內的 i 相乘得到結果。return
值是一個匿名函數。有沒有注意到,為什麼 timesX(3)
中的 3 不會與 timesX(5)
互相影響 (兩個參數都傳給了 timesX 的 multiplicand)。
→ 這稱為語彙封包 (Lexical Closures):timesX(int multiplicand)
中的參數因為被包在大括弧裡面,所以其作用域只有在括弧括起來的範圍。即使使用相同的函數名稱宣告變數,還是算是不同的語彙封包。
由於 Dart 的 函數 (Function) 是物件 (Object),所以我們可以將函數當作變數,作為函數的參數,甚至當作函數的回傳值。
作為函數的參數,我們可以使用匿名函數,省去不必要的函數宣告。
作為回傳值,我們可以將不同的變數帶入,創造出相似卻又不同的函數,因為所有帶入的參數都會因為語彙封包而互不影響。