[TOC]
Dart是一種真正的面向對象語言,所以即使函數也是對象,具有類型和功能。這意味著函數可以分配給變量或作為參數傳遞給其他函數。您還可以像調用函數一樣調用Dart類的實例。有關詳細信息,請參見[可調用類]。
這里有一個實現函數的例子:
~~~
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
~~~
盡管Effective Dart建議對公共api使用類型注釋,但是如果您省略了類型,這個函數仍然可以工作:
~~~
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
~~~
對于只包含一個表達式的函數,可以使用簡寫語法:
~~~
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
~~~
=>expr 語法是{return expr;}的簡寫寫法。“=>”符號有時被稱為胖箭頭語法。
>注意:在箭頭(=>)和分號(;)之間只能出現一個表達式(不是語句)。例如,您不能在那里放一個if語句,但是您可以使用一個條件表達式。
>
函數可以有兩種類型的參數:必需的和可選的。首先列出必須的參數,后邊是任何可選參數。命名可選參數也可以標記為@required。有關細節,請參見下一節。
## 可選參數
可選參數可以是位置參數,也可以是命名參數,但不能兩者都是。
### 可選的命名參數
在調用函數時,可以使用paramName: value來指定命名參數。例如:
~~~
enableFlags(bold: true, hidden: false);
~~~
在定義函數時,使用{param1, param2,…}來指定命名參數:
~~~
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold, bool hidden}) {...}
~~~
Flutter實例創建表達式可能會變得復雜,因此小部件構造函數只使用命名參數。這使得實例創建表達式更容易閱讀。
您可以在任何Dart代碼(不僅僅是Flutter)中注釋一個已命名的參數,并使用@required說明它是一個必傳的參數。例如:
~~~
const Scrollbar({Key key, @required Widget child})
~~~
當構造Scrollbar時,分析器在沒有子參數時報錯。
Required在元包中被定義。或者直接使用import package:meta/meta.dart導入或者導入其他包含meta導出的包,例如Flutter的包:Flutter /material.dart。
### 可選的位置參數
在[]中包裝一組函數參數,標記為可選的位置參數:
~~~
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
~~~
這里有一個調用這個函數的例子,沒有可選參數:
~~~
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
~~~
這里有一個用第三個參數調用這個函數的例子:
~~~
assert(say('Bob', 'Howdy', 'smoke signal') ==
'Bob says Howdy with a smoke signal');
~~~
### 默認參數值
您的函數可以使用 = 來定義命名和位置參數的默認值。默認值必須是編譯時常量。如果沒有提供默認值,則默認值為null。
下面是為命名參數設置默認值的示例:
~~~
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold = false, bool hidden = false}) {...}
// bold will be true; hidden will be false.
enableFlags(bold: true);
~~~
>棄用注釋:舊代碼可能使用冒號(:)而不是=來設置命名參數的默認值。原因是最初,只有:被命名參數支持。這種支持很可能被棄用,因此我們建議您使用=來指定默認值。
>
下一個示例展示了如何設置位置參數的默認值:
~~~
String say(String from, String msg,
[String device = 'carrier pigeon', String mood]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
if (mood != null) {
result = '$result (in a $mood mood)';
}
return result;
}
assert(say('Bob', 'Howdy') ==
'Bob says Howdy with a carrier pigeon');
~~~
您還可以將列表或map集合作為默認值。下面的示例定義一個函數doStuff(),該函數指定列表參數的默認列表和禮品參數的默認map集合。
~~~
void doStuff(
{List<int> list = const [1, 2, 3],
Map<String, String> gifts = const {
'first': 'paper',
'second': 'cotton',
'third': 'leather'
}}) {
print('list: $list');
print('gifts: $gifts');
}
~~~
## main() 函數
每個應用程序都必須有一個頂級的main()函數,它作為應用程序的入口點。main()函數返回void,并有一個可選的列表參數作為參數。
下面是web應用程序的main()函數示例:
~~~
void main() {
querySelector('#sample_text_id')
..text = 'Click me!'
..onClick.listen(reverseText);
}
~~~
>注意:代碼前的(..)語法被稱為級聯。使用級聯,可以對單個對象的成員執行多個操作。
>
下面是一個命令行應用程序的main()函數示例,它接受參數:
~~~
// Run the app like this: dart args.dart 1 test
void main(List<String> arguments) {
print(arguments);
assert(arguments.length == 2);
assert(int.parse(arguments[0]) == 1);
assert(arguments[1] == 'test');
}
~~~
您可以使用args庫來定義和解析命令行參數。
## 函數是一等公民(Functions as first-class objects)
>譯者注:這個思考了好久還是意譯為這個,因為在JavaScript的參考資料中看見過這樣的翻譯,想來也非常有道理。
>
可以將函數作為參數傳遞給另一個函數。例如:
~~~
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
// Pass printElement as a parameter.
list.forEach(printElement);
~~~
你也可以為變量分配一個函數,例如:
~~~
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
~~~
這個例子使用一個匿名函數。下一節將詳細介紹這些內容。
## 匿名函數
You can also create a nameless function called an anonymous function, or sometimes a lambda or closure. You might assign an anonymous function to a variable so that, for example, you can add or remove it from a collection.
An anonymous function looks similar to a named function— zero or more parameters, separated by commas and optional type annotations, between parentheses.
The code block that follows contains the function’s body:
大多數函數都被命名,如main()或printElement()。你也可以創建一個沒有函數名稱的函數,這種函數稱為匿名函數,或者lambda函數或者閉包函數。您可以為變量分配一個匿名函數,例如,您可以從集合中添加或刪除它。
匿名函數看起來類似于命名函數--有0個或者多個參數,在括號之間用逗號和可選類型標注分隔。后面的代碼塊包含函數的主體:
~~~
([[Type] param1[, …]]) {
codeBlock;
};
~~~
下面的示例定義了一個帶有無類型參數item的匿名函數。該函數為列表中的每個item調用,打印一個字符串,該字符串包含指定索引的值。
~~~
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
print('${list.indexOf(item)}: $item');
});
~~~
真機演示結果:
~~~
void main() {
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
print('${list.indexOf(item)}: $item');
});
}
///結果:
0: apples
1: bananas
2: oranges
~~~
如果函數只包含一條語句,可以使用胖箭頭符號來縮短它。
~~~
list.forEach(
(item) => print('${list.indexOf(item)}: $item'));
~~~
## 詞法作用域
Dart是一種在詞法上確定范圍的語言,這意味著變量的范圍是靜態的,僅僅是通過代碼的布局來決定的。您可以“跟隨花括號向外”以查看變量是否在范圍內。
這里有一個嵌套函數的例子,每個作用域級別上都有變量:
~~~
bool topLevel = true;
void main() {
var insideMain = true;
void myFunction() {
var insideFunction = true;
void nestedFunction() {
var insideNestedFunction = true;
assert(topLevel);
assert(insideMain);
assert(insideFunction);
assert(insideNestedFunction);
}
}
}
~~~
請注意nestedFunction()如何使用從每個級別到頂層的變量。
## 詞法閉包
閉包是一個函數對象,它可以訪問其詞法范圍內的變量,即使函數在其原始范圍之外使用。
函數可以關閉周圍作用域中定義的變量。在下面的示例中,makeAdder()捕獲變量addBy。無論返回的函數到哪里,它都會記住addBy。
~~~
/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(num addBy) {
return (num i) => addBy + i;
}
void main() {
// Create a function that adds 2.
var add2 = makeAdder(2);
// Create a function that adds 4.
var add4 = makeAdder(4);
assert(add2(3) == 5);
assert(add4(3) == 7);
}
~~~
## 判斷函數相等
下面是一個測試頂級函數、靜態方法和相等實例方法的示例:
~~~
void foo() {} // A top-level function
class A {
static void bar() {} // A static method
void baz() {} // An instance method
}
void main() {
var x;
// Comparing top-level functions.
x = foo;
assert(foo == x);
// Comparing static methods.
x = A.bar;
assert(A.bar == x);
// Comparing instance methods.
var v = A(); // Instance #1 of A
var w = A(); // Instance #2 of A
var y = w;
x = w.baz;
// These closures refer to the same instance (#2),
// so they're equal.
assert(y.baz == x);
// These closures refer to different instances,
// so they're unequal.
assert(v.baz != w.baz);
}
~~~
## 返回值
所有函數都返回一個值。如果沒有指定返回值,則語句返回null;隱式附加到函數體。
~~~
foo() {}
assert(foo() == null);
~~~