參見: Dart Functions
在Dart使用函數上,針對只有一個函數的表達式我們可以用=> expr
的方式作為簡寫。像是下列程式碼只須回傳一個 " _nobleGases[ atomicNumber ] " 變數值
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
單一回傳值函數
而在這樣的函式,我們就可以簡寫為下列方式,並且需要注意,=>
後到分號前我們只能使用條件式表達
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
單一回傳值函數的箭頭表示法簡寫
required
代表是"必要(不得缺乏)"的參數{param1, param2, …}
在本範例中,我們宣告一個函數"printPersonInfo",並使用命名參數並給予初始值,而在main()函式呼叫時,我們分為三種不同的參數給予情境,
void printPersonInfo({String name = "name", int age = 10, String location = "Unknown"}) {
print("Name: $name, Age: $age, Location: $location");
}
void main() {
// 使用命名參數調用函式
printPersonInfo(name: "Alice", age: 30);
// 只提供一部分參數
printPersonInfo(name: "Bob");
// 提供所有參數
printPersonInfo(name: "Charlie", age: 25, location: "London");
}
命名參數使用範例
而若是我們稍微更改程式碼,將name變數前綴required
指令,由下圖可看到若我們沒有在呼叫函式給予name參數,便會產生報錯
[]
包含起來的參數代表"可選位置參數"在下列範例中,我們使用中括號將"location"變數設為可選位置參數,因此在三個呼叫範例中,我們可以看到使用與不使用預設值情況下的location變數輸出結果的相異
void printPersonInfo(String name, int age, [String location = "Unknown"]) {
print("Name: $name, Age: $age, Location: $location");
}
void main() {
// 提供所有參數
printPersonInfo("Alice", 30, "New York");
// 只提供前兩個參數,location 使用預設值
printPersonInfo("Bob", 25);
// 提供所有參數,location 覆蓋預設值
printPersonInfo("Charlie", 28, "London");
}
可選位置參數使用範例
基本上,大家應該在前幾天的範例中已大量使用dartpad進行程式碼的操作,對main()函式的使用應該不陌生,一般而言,一個dart程式檔案內(或未來我們使用,一個flutter專案內)都會有一個main()函數作為我們主要執行邏輯的觸發,並呼叫其他函數或是進行其他操作
void main() {
// 在這裡放置程式的主要執行邏輯
print("Hello, World!"); // 例如這裡輸出一條訊息
}
main()函數基本範例
我們可以將參數列表放在小括號中,然後在大括號內撰寫函數主體。這個範例中,我們使用匿名函數作為 forEach 方法的參數。這個匿名函數將列表中的每個元素平方並輸出結果。匿名函數的定義包含在大括號中,並在 forEach 中傳遞
void main() {
var numbers = [1, 2, 3, 4, 5];
// 使用匿名函數將列表中的每個元素平方並印出
numbers.forEach((number) {
var squared = number * number;
print('$number 的平方值是 $squared');
});
}
匿名函數使用範例
而我們也可以利用箭頭語法的方式省略上述範例如下,不過因為匿名函數的參數只能在內部有效,因此我們僅輸出平方值作為參考
void main() {
const list = [1, 2, 3, 4, 5];
list
.map((number) => number * number)
.forEach((squared) => print('Squared: $squared'));
}
匿名函數使用,箭頭語法簡略範例
「函數詞彙範圍」指變數在不同函數之間的可見性和存取範圍。每個函數在其內部都有一個自己的詞彙範圍,這意味著在函數內部聲明的變數只能在該函數內部使用,且不會影響到其他函數。在一般靜態的宣告中我們可以藉由依據大括號的{}
的前後位置來判定
在函數內部宣告的變數稱為區域變數。這些變數只在「該函數」的詞彙範圍內可見,不能在其他函數中訪問。當函數執行完成後,區域變數的生命週期也會結束,它們的值將被釋放。
在任何函數外部宣告的變數被稱為全域變數。這些變數在「整個程式」中都是可見的,可以在不同的函數內部訪問和修改。全域變數的生命週期在整個程式運行期間持續。
函數的參數也具有詞彙範圍,它們只在函數內部有效。函數的參數在「函數內部】有效。函數的參數在函數內部的詞彙範圍中扮演著區域變數的角色。
在一個函數內部定義的另一個函數被稱為巢狀函數。巢狀函數可以訪問其外部函數的區域變數,但外部函「無法」訪問巢狀函數的區域變數。
在下列範例中,在這個例子中,"inner 函數"是一個詞彙閉包,捕獲了 "outer 函數"的詞彙範圍內的 "outerValue" 和 "innerValue" 變數。即使 "inner 函數"在 "outer 函數"回傳後才被調用,它仍然可以訪問和使用這兩個變數的值。
Function outer() {
int outerValue = 10;
Function inner() {
int innerValue = 20;
return () {
print("Outer value: $outerValue, Inner value: $innerValue"); //輸出outerValue與innerValue變數值
};
}
return inner;
}
void main() {
var closure = outer();
var innerClosure = closure();
innerClosure(); // 輸出 "Outer value: 10, Inner value: 20"
}
詞彙閉包使用範例
Iterable
對象Stream
對象在同步生成器中,使用Iterable
與sync*
來宣告函數,下方例子中,並用yield
關鍵字產生值並暫停函數的執行,然後可以從暫停的地方恢復
Iterable<int> naturalNumbers(int n) sync* {
for (int i = 1; i <= n; i++) {
yield i;
}
}
在同步生成器中,使用Stream
與async*
來宣告函數,並用await
關鍵字等待異步操作的完成,再用yield
關鍵字產生值
Stream<int> asyncNaturalNumbers(int n) async* {
for (int i = 1; i <= n; i++) {
await Future.delayed(Duration(seconds: 1)); // 模擬異步操作的延遲時間
yield i;
}
}
以下是我們結合同步/異步產生器的宣告式與使用範例
Iterable<int> naturalNumbers(int n) sync* {
for (int i = 1; i <= n; i++) {
yield i;
}
}
Stream<int> asyncNaturalNumbers(int n) async* {
for (int i = 1; i <= n; i++) {
await Future.delayed(Duration(seconds: 1)); // 模擬異步操作的延遲時間,這裡訂為1秒鐘
yield i;
}
}
void main() {
final n = 5;
print("Sync Generator:"); //同步產生器
final syncNumbers = naturalNumbers(n);
for (var number in syncNumbers) {
print(number);
}
print("\nAsync Generator:"); //異步產生器
final asyncNumbers = asyncNaturalNumbers(n);
asyncNumbers.listen((number) {
print(number);
});
}
同步/異步產生器範例