Dart是一個真-面嚮對象語言,因此甚至連 functions 都是objects,而且有一個類型叫 Function
。 這表示functons能夠被變量指定或者以參數的方式傳遞給其餘functions。若是Dart類型實例是一個function的話你也能夠直接調用。web
Demo:markdown
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
複製代碼
雖然Effective Dart建議爲公共API註釋類型,但若是省略類型,該函數仍然有效:閉包
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
複製代碼
對於只包含一個表達式的函數,可使用簡寫語法:app
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
複製代碼
=> expr
標誌是 { return expr; }
的縮寫,=>
表示法有時稱爲箭頭語法。ide
注意:箭頭(=>)和分號(;)之間只能出現表達式而非語句。 例如,您不能在其中放置if語句,但可使用條件表達式。函數
函數能夠有兩種類型的參數:必需和可選。 首先列出所需參數,而後列出任何可選參數。 可選參數能夠是命名的或位置的。佈局
注意:某些API(特別是Flutter widget構造函數)僅命名參數,即便對於必需參數也是如此。測試
可選參數能夠是命名參數,也能夠是位置參數,但不能同時知足ui
調用函數時,你可使用 paramName:value
這樣的形式指定命名參數。 例如:this
enableFlags(bold: true, hidden: false);
複製代碼
定義函數時,使用{param1,param2,...}
去指定命名參數:
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold, bool hidden}) {...}
複製代碼
儘管命名參數是一種可選參數,但您可使用@required
對它們進行註釋,以指示該參數是必需的 - 用戶必須爲參數提供值。 例如:
const Scrollbar({Key key, @required Widget child})
複製代碼
若是有人嘗試建立Scrollbar
而未指定child
的參數,解析器就會報錯。 要使用@required
註釋,請依賴於meta
包和導入 package:meta/meta.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');
複製代碼
在function 中用 =
來定義參數默認值,命名參數和位置參數同時適用。 默認值必須是編譯時常量,若是沒有提供默認值,默認是 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');
複製代碼
你也能夠傳遞 lists 或者 maps 做爲默認值,demo以下:
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');
}
複製代碼
每一個app都必須有一個頂級的 main()
函數做爲一個app的入口服務。 main()
函數返回void
而且有一個可選參數List<String>
。
一個 web app 的main()
函數例子:
void main() {
querySelector('#sample_text_id')
..text = 'Click me!'
..onClick.listen(reverseText);
}
複製代碼
注意:前面代碼中的
..
語法稱爲級聯。 使用級聯,能夠對單個對象的成員們執行多個操做。
一個 command-line app 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 lib 依賴去定義和解析 command-line 參數。
你能夠把function做爲一個參數傳遞給另一個function, 例如:
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
// Pass printElement as a parameter.
list.forEach(printElement);
複製代碼
你也能夠把一個function 指定給一個變量,例如:
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
複製代碼
這個例子用了一個匿名函數。更多關於匿名函數的知識,隨後分解。
大對數的函數都是有名字的,例如main()
或者printElement()
。固然你也能夠建立沒有名字的函數(匿名函數),或者有時候一個 lambda 或者 closure(閉包)。你可能把一個匿名函數賦值給一個變量,例如,你能夠從一個集合中添加或者刪除。 一個匿名函數跟命名函數看上去很類似 - 0個或更多的參數,由逗號分開,還有可選的類型註釋, 位於括號之間。
如下的代碼塊包含了函數體:
([[Type] param1[, …]]) {
codeBlock;
};
複製代碼
如下示例定義一個匿名函數,使用無類型參數 item
。 這個函數打印list中每一個字符串,而且輸出字符串在list中的索引
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);
複製代碼