函數(shù)(Functions)
Dart是一個(gè)真正的面向?qū)ο笳Z(yǔ)言,方法也是對(duì)象并且具有一種類型 Function。 這意味著,方法可以賦值給變量,也可以當(dāng)做其他方法的參數(shù)。 也可以把Dart類的實(shí)例當(dāng)做方法來(lái)調(diào)用。 詳情請(qǐng)參考Callable classes.
下面是定義方法的示例:
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
雖然在Effective Dart中推薦type annotations for public APIs, 你當(dāng)然也可以選擇忽略類型定義:
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
對(duì)于只有一個(gè)表達(dá)式的方法,你可以選擇使用縮寫(xiě)語(yǔ)法來(lái)定義:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
這個(gè) => expr 語(yǔ)法是 { return expr; }形式的縮寫(xiě)。=>形式有時(shí)候也稱之為箭頭語(yǔ)法。
注意: 在箭頭 (=>)和分號(hào)(;)之間只能使用一個(gè)表達(dá)式 – 不能使用語(yǔ)句。 例如:你不能使用 if statement,但是可以使用條件表達(dá)式conditional expression.
方法可以有兩種類型的參數(shù):必需的(required)和可選的(optional)。 必需的參數(shù)在參數(shù)列表前面, 后面是可選參數(shù)。被命名為可選的參數(shù)也能被標(biāo)記為@required。
可選參數(shù)(Optional parameters)
可選參數(shù)可以是命名參數(shù)或者基于位置的參數(shù),但是這兩種參數(shù)不能同時(shí)當(dāng)做可選參數(shù)。
可選命名參數(shù)(Optional named parameters)
調(diào)用方法的時(shí)候,你可以使用這種形式 paramName: value 來(lái)指定命名參數(shù)。例如:
enableFlags(bold: true, hidden: false);
在定義方法的時(shí)候,使用 {param1, param2, …}的形式來(lái)指定命名參數(shù):
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold, bool hidden}) {...}
Flutter實(shí)例創(chuàng)建表達(dá)式能夠變得非常復(fù)雜,所以widget的構(gòu)造器使用了命名參數(shù),這樣創(chuàng)建實(shí)例易于閱讀。
我們也可以在任意的Dart代碼中使用命名參數(shù),使用@required表示該參數(shù)是必須的。
const Scrollbar({Key key, @required Widget child})
當(dāng)Scrollbar被創(chuàng)建的時(shí)候,如果child參數(shù)缺少將會(huì)報(bào)錯(cuò)。Required是被定義在meta package中。所以要么直接倒入package:meta/meta.dart,或者其他package中有倒入meta,比如:Flutter的package:flutter/material.dart.
可選位置參數(shù)(Optional positional parameters)
把一些方法的參數(shù)放到[]中就變成可選位置參數(shù)了:
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
下面是不使用可選參數(shù)調(diào)用上面方法 的示例:
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
下面是使用第三個(gè)可選參數(shù)調(diào)用上面方法的示例:
assert(say('Bob', 'Howdy', 'smoke signal') ==
'Bob says Howdy with a smoke signal');
默認(rèn)參數(shù)值(Default parameter values)
在定義方法的時(shí)候,可以使用=為命名和位置參數(shù)來(lái)定義默認(rèn)值。 默認(rèn)值必須是編譯時(shí)常量。 如果沒(méi)有提供默認(rèn)值,則默認(rèn)值為null。
下面是為命名參數(shù)設(shè)置默認(rèn)值的示例:
/// 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);
版本問(wèn)題: 老版本代碼可能需要使用一個(gè)冒號(hào) (:) 而不是 = 來(lái)設(shè)置參數(shù)默認(rèn)值。 : 設(shè)置命名默認(rèn)參數(shù)值在以后版本中將不能使用, 所以推薦你 **使用 = 來(lái)設(shè)置默認(rèn)值
下面的示例顯示了如何設(shè)置位置參數(shù)的默認(rèn)值:
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');
還可以使用list或者map作為默認(rèn)值。 下面的示例定義了一個(gè)方法 doStuff(), 并分別為list和gifts參數(shù)指定了 默認(rèn)值。
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');
}
入口函數(shù)(The main() function)
每個(gè)應(yīng)用都需要有個(gè)頂級(jí)的main()入口方法才能執(zhí)行。 main()方法的返回值為void并且有個(gè)可選的List<String>參數(shù)。
下面是一個(gè)web應(yīng)用的main()方法:
void main() {
querySelector('#sample_text_id')
..text = 'Click me!'
..onClick.listen(reverseText);
}
注意: 前面代碼中的 .. 語(yǔ)法為cascade.。 使用級(jí)聯(lián)調(diào)用語(yǔ)法, 你可以在一個(gè)對(duì)象上執(zhí)行多個(gè)操作。
下面是一個(gè)命令行應(yīng)用的main()方法,并且使用了方法參數(shù)作為輸入?yún)?shù):
// 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 library來(lái)定義和解析命令行輸入的參數(shù)數(shù)據(jù)。
一等方法對(duì)象(Functions as first-class objects)
可以把方法當(dāng)做參數(shù)調(diào)用另外一個(gè)方法。例如:
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
// Pass printElement as a parameter.
list.forEach(printElement);
方法也可以賦值給一個(gè)變量:
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
上面的方法為,下面將介紹的匿名方法。
匿名方法(Anonymous functions)
大部分方法都帶有名字,例如main()或者printElement()。 你有可以創(chuàng)建沒(méi)有名字的方法,稱之為匿名方法,有時(shí)候也被稱為lambda或者 closure閉包。 你可以把匿名方法賦值給一個(gè)變量,比如:添加到集合或者從集合中刪除。
匿名函數(shù)形式如下,大括號(hào)中的代碼為函數(shù)體:
([[Type] param1[, …]]) {
//codeBlock;
};
下面的代碼定義了一個(gè)參數(shù)為item(該參數(shù)沒(méi)有指定類型)的匿名函數(shù)。list中的每個(gè)元素都會(huì)調(diào)用這個(gè)函數(shù)來(lái)打印出來(lái),同時(shí)來(lái)計(jì)算了每個(gè)元素在 list中的索引位置。
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
print('${list.indexOf(item)}: $item'); // 0: apples 1: bananas 2: oranges
});
如果方法只包含一個(gè)語(yǔ)句,可以使用箭頭語(yǔ)法縮寫(xiě)
list.forEach(
(item) => print('${list.indexOf(item)}: $item'));
靜態(tài)作用域(Lexical scope)
Dart是靜態(tài)作用域語(yǔ)言,變量的作用域在寫(xiě)代碼的時(shí)候就確定過(guò)了。 基本上大括號(hào)里面定義的變量就只能在大括號(hào)里面訪問(wèn)
下面是嵌套函數(shù)顯示變量作用域的一個(gè)示例:
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() 可以訪問(wèn)所有的變量, 包含頂級(jí)變量。
詞法閉包(Lexical closures)
閉包是方法對(duì)象,不管該對(duì)象在何處被調(diào)用, 該對(duì)象都可以訪問(wèn)其作用域內(nèi)的變量。
方法可以封閉定義到其作用域內(nèi)的變量。 下面的示例中,makeAdder()捕獲到了變量 addBy。 不管你在那里執(zhí)行 makeAdder()所返回的函數(shù),都可以使用addBy參數(shù)。
/// 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);
}
測(cè)試函數(shù)是否相等(Testing functions for equality)
下面是測(cè)試頂級(jí)方法、靜態(tài)函數(shù)和實(shí)例函數(shù) 相等的示例:
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);
}
返回值(Return values)
所有的函數(shù)都返回一個(gè)值。如果沒(méi)有指定返回值,則默認(rèn)把語(yǔ)句 return null; 作為函數(shù)的最后一個(gè)語(yǔ)句執(zhí)行。
foo() {}
assert(foo() == null);