一、 Functions (函數(shù))
Dart 是一個(gè)真正的面向?qū)ο笳Z(yǔ)言,函數(shù)也是一個(gè)對(duì)象并且具有其類型:Function。這意味著,方法可以賦值給變量,也可以當(dāng)做其他方法的參數(shù)。您還可以像調(diào)用函數(shù)一樣調(diào)用Dart類的實(shí)例。詳情請(qǐng)參考 Callable classes。
Dart是不支持函數(shù)的重載的****,一個(gè)類中不允許存在同名的函數(shù)出現(xiàn),這點(diǎn)與C語(yǔ)言一樣。雖然不支持函數(shù)重載但是它支持可選參數(shù)的使用。
下面是定義方法的示例:
bool isNoble(int atomicNumber) {
return atomicNumber != null;
}
Dart中函數(shù)的返回值類型 和 參數(shù)類型都可以省略****,但是Effective Dart 中推薦最好不要省略:
isNoble(atomicNumber) {
return atomicNumber != null;
}
對(duì)于只包含一個(gè)表達(dá)式的函數(shù),可以使用簡(jiǎn)寫語(yǔ)法:(return必須省略)
bool isNoble(int atomicNumber) => atomicNumber != null; //return必須省略
//同樣可以省略類型,下面等價(jià)
isNoble(atomicNumber) => atomicNumber != null;
這個(gè) => expr 語(yǔ)法是 { return expr; } 形式的縮寫。=> 形式 有時(shí)候也稱之為 箭頭語(yǔ)法( arrow syntax)。
注意: 在箭頭 => 和冒號(hào) ; 之間只能使用一個(gè)表達(dá)式(expression) ,而不能使用語(yǔ)句(statement)。 例如:你不能使用 if statement,但是可以 使用條件表達(dá)式 conditional expression。
方法可以有兩種類型的參數(shù):必需的和可選的。命名可選參數(shù)也可以標(biāo)記為@required。
1.1、可選參數(shù)
可選參數(shù)可以是基于命名參數(shù)或者基于位置的參數(shù),但是這兩種參數(shù)不能同時(shí)當(dāng)做可選參數(shù)。
如果參數(shù)列表中同時(shí)存在可選參數(shù) 和 必選參數(shù),必選參數(shù)必需放在可選參數(shù)前面!
Optional named parameters(可選命名參數(shù))
在定義方法的時(shí)候,使用 {param1, param2, …} 的形式來指定命名可選參數(shù)。
在調(diào)用方法的時(shí)候,你可以使用這種形式 paramName: value 來指定命名參數(shù),而不能直接傳值:
//函數(shù)定義,注意有{}
enableFlags({bool bold, bool hidden}) {
// ...
}
//函數(shù)調(diào)用
enableFlags(hidden: false,bold: true);//OK
enableFlags(bold: true);//OK
enableFlags();//OK
//enableFlags(false, true);//編譯錯(cuò)誤
{}表示可選參數(shù),當(dāng)然你也可以按需傳入可選參數(shù),但是必選參數(shù)必選要傳遞:
//a是必選參數(shù), {}中的是可選參數(shù)
//void enableFlags({bool bold, bool hidden},int a) //錯(cuò)誤定義,可選參數(shù)只能放在后面
void enableFlags(int a,{bool bold, bool hidden}) {
print(bold.toString()+" "+hidden.toString());
}
main() {
enableFlags(3,bold: true); //true null
enableFlags(3); //null null
//enableFlags(); 錯(cuò)誤。有必選參數(shù)a存在,不能不傳任何參數(shù)
}
注意,可選參數(shù)只能放參數(shù)列表的在最后。
Optional positional parameters(可選位置參數(shù))
把一些方法的參數(shù)放到 [ ] 中就變成可選 位置參數(shù)了,可選位置參數(shù)只能根據(jù)位置選擇傳入的參數(shù),而不能通過命名的方式,其他的效果與可選命名參數(shù)一致:
//[]表示可選位置參數(shù)
void enableFlags(int a,[bool hidden, bool bold]) {
print(bold.toString()+" "+hidden.toString());
}
main() {
enableFlags(3,true,true); //true true
enableFlags(3,true); //null true
}
Default parameter values(默認(rèn)參數(shù)值)
在定義方法的時(shí)候,可以使用 = 來定義可選參數(shù)的默認(rèn)值,只能用于可選參數(shù)(兩種可選參數(shù)[ ]和{}都支持)。 默認(rèn)值只能是編譯時(shí)常量。 如果沒有提供默認(rèn)值,則默認(rèn)值為 null (Dart中萬(wàn)物都是對(duì)象,沒有基本類型之分)。
void enableFlags(int a ,{bool hidden = true, bool bold }) {
print(bold.toString()+" "+hidden.toString());
}
main() {
enableFlags(3); //null true
enableFlags(3,hidden: false); //null false
enableFlags(3,hidden: false,bold: false); //false false
}
版本問題: 舊版本代碼可能需要使用一個(gè)冒號(hào) (:) 而不是 = 來設(shè)置參數(shù)默認(rèn)值。 原因在于 Dart SDK 1.21 之前的版本,命名參數(shù)只支持 :。 : 設(shè)置命名默認(rèn)參數(shù)值在以后版本中將不能使用, 所以我們推薦你 使用 = 來設(shè)置默認(rèn)值, 并 指定 Dart SDK 版本為 1.21 或者更高的版本。
還可以使用 list 或者 map 作為默認(rèn)值。 下面的示例定義了一個(gè)方法 doStuff(), 參數(shù)類型分別為list和map,并分別為其指定了默認(rèn)值,由于默認(rèn)值必須是編譯時(shí)常量,所以必須使用const修飾:
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() {
doStuff();
}
1.2、main函數(shù)
每個(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);
}
下面是一個(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');
}
1.3、函數(shù)是一等公民(Functions as first-class objects)
函數(shù)可以作為參數(shù)來進(jìn)行調(diào)用,例如:
printElement(element) {
print(element);
}
main() {
var list = [1, 2, 3];
list.forEach(printElement);
}
輸出結(jié)果為:1、2、3。 我們可以看看forEach的函數(shù)定義,直接聲明一個(gè)函數(shù)的聲明作為函數(shù)的參數(shù):

我們也可以為變量分配一個(gè)函數(shù),例如下面:(這個(gè)例子使用一個(gè)匿名函數(shù)。下一節(jié)將詳細(xì)介紹這些內(nèi)容)
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
1.4、Anonymous functions(匿名函數(shù))
你有可以創(chuàng)建沒有函數(shù)名的函數(shù),稱之為匿名函數(shù),有時(shí)候也被稱為 lambda 或者 closure 閉包。 你可以把匿名方法賦值給一個(gè)變量, 然后你可以使用這個(gè)方法,比如添加到集合或者從集合中刪除。您可以為變量分配一個(gè)匿名函數(shù),例如,然后你可以使用這個(gè)方法,比如添加到集合或者從集合中刪除。
匿名函數(shù)看起來類似于命名函數(shù)—有0個(gè)或者多個(gè)參數(shù),在括號(hào)之間用逗號(hào)和可選類型標(biāo)注分隔。后面的代碼塊包含函數(shù)的主體:
([[Type] param1[, …]]) {
codeBlock;
};
下面的示例定義了一個(gè)無類型參數(shù)item的匿名函數(shù)。list 中的每個(gè)元素都會(huì)調(diào)用這個(gè)函數(shù)來 打印出來,同時(shí)來計(jì)算了每個(gè)元素在 list 中的索引位置:
var list = ['apples', 'bananas', 'oranges'];
//1、匿名函數(shù)允許直接賦予給一個(gè)變量
var a = (item) {
print('${list.indexOf(item)}: $item');
};
list.forEach(a);//a指向一個(gè)匿名函數(shù)
//2、也允許直接傳入一個(gè)匿名函數(shù),與上面的兩句效果是等價(jià)的
list.forEach((item) {
print('${list.indexOf(item)}: $item');
});
/*打印結(jié)果
0: apples
1: bananas
2: oranges
*/
如果函數(shù)只包含一個(gè)語(yǔ)句,可以使用箭頭語(yǔ)法縮寫:
var list = ['apples', 'bananas', 'oranges'];
var a = (item) => print('${list.indexOf(item)}: $item');
list.forEach(a);
//與上面兩句等價(jià)
list.forEach((item) => print('${list.indexOf(item)}: $item'));
1.5、Lexical scope(靜態(tài)作用域)
Lexical Scope (also called Static Scope)。Dart 是靜態(tài)作用域語(yǔ)言,變量的作用域在寫代碼的時(shí)候就確定過了。 基本上大括號(hào)里面定義的變量就只能在大括號(hào)里面訪問,和 Java 作用域類似。
這里有一個(gè)嵌套函數(shù)的例子,每個(gè)作用域級(jí)別上都有變量:
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() 可以訪問所有的變量, 包含頂級(jí)變量。
1.6、Lexical closures(詞法閉包)
一個(gè) 閉包(匿名函數(shù)) 是一個(gè)函數(shù)對(duì)象,不管該對(duì)象在何處被調(diào)用, 該對(duì)象都可以訪問其作用域內(nèi)的變量。
在下面的示例中,makeAdder()捕獲變量addBy。無論返回的函數(shù)到哪里,它都會(huì)記住addBy:
/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(num addBy) {
return (num i) => addBy + i; //匿名函數(shù)作為Function類型的對(duì)象進(jìn)行返回
}
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);
}
在上面的示例中我們可以看出:普通函數(shù),匿名函數(shù)都是一個(gè)Function類型的對(duì)象,并且可以作為返回值進(jìn)行返回。
例如下面例子,add2指向的就是test函數(shù)對(duì)象,當(dāng)執(zhí)行add2()的時(shí)候?qū)嶋H上調(diào)用的是test()函數(shù):
test(){
print("Hello World");
}
Function makeAdder(addBy) {//返回類型可以省略
return test ;
}
main() {
var add2 = makeAdder(2);
add2(); //Hello World
}
1.7、判斷函數(shù)相等
下面是測(cè)試頂級(jí)方法、靜態(tài)函數(shù)和實(shí)例函數(shù) 相等的示例:(下面都是執(zhí)行通過的)
foo() {} // A top-level function
class A {
static void bar() {} // A static method
void baz() {} // An instance method
}
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 = new A(); // Instance #1 of A
var w = new 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);
}
上面的例子可以看出 同一個(gè)類的不同實(shí)例之間,對(duì)于同一個(gè)方法來說其對(duì)象是不相等的,既var v = A() 和 var w = A() 中 v.bar 與 w.bar 方法對(duì)象是不相等的。
1.8、Return values(返回值)
所有函數(shù)都返回一個(gè)值。如果沒有指定返回值,則 默認(rèn)把語(yǔ)句 return null,作為函數(shù)的最后一個(gè)語(yǔ)句執(zhí)行。
foo() {}
assert(foo() == null);
二、異常
代碼中可以出現(xiàn)異常和捕獲異常。異常表示一些 未知的錯(cuò)誤情況。如果異常沒有捕獲, 則異常會(huì)拋出,導(dǎo)致 拋出異常的代碼終止執(zhí)行。
和 Java 不同的是,所有的 Dart 異常是非檢查異常 unchecked exceptions(既不需要一定要用try ...catch處理)。方法不聲明它們可能拋出哪些異常,也不要求您捕獲任何異常:
test(){//方法體也不用聲明
throw new FormatException("Test one ");
}
void main() {
test();//不需要try-catch
}
Dart 提供了 Exception 和 Error 類型, 以及一些子類型。你還 可以定義自己的異常類型。但是, Dart 代碼可以拋出任何非 null 對(duì)象為異常,不僅僅是實(shí)現(xiàn)了 Exception 或者 Error 的對(duì)象。( Dart拋出的異常類型不一定要是Exception和Error類型,任何非空類型都可以)。
2.1、throw
下面是拋出一個(gè)異常的示例:
throw new FormatException('Expected at least 1 section');//FormatException是Exception的子類
你也可以拋出任意對(duì)象:
throw 'Out of llamas!';
注意:在正式使用中通常拋出的是實(shí)現(xiàn)了Error或Exception類型的對(duì)象;
由于拋出異常是一個(gè)表達(dá)式,所以可以在 => 語(yǔ)句中使用,也可以在其他能使用表達(dá)式的地方拋出異常:
void distanceTo(Point other) => throw UnimplementedError();
2.2、Catch
與java一樣,Dart中可以使用 try-catch來捕獲異常,避免異常繼續(xù)傳遞(除非你重新拋出rethrow異常)。不過與java不同的事,使用on ExceptionType來聲明捕獲具體的異常類型。on ExceptionType catch(e) 通過catch可以捕獲異常對(duì)象e。如果沒有指定異常類型,直接使用catch(e),則表示可以捕獲任何異常類型。
只捕獲OutOfLlamasException異常:
try {
breedMoreLlamas();
} on OutOfLlamasException {
buyMoreLlamas();
}
對(duì)于可以拋出多種類型異常的代碼,你可以指定 多個(gè)捕獲語(yǔ)句:
void main() {
try {
// throw new FormatException("Hello"); 只觸發(fā)第一個(gè)on FormatException
// throw new TimeoutException("hello");只觸發(fā)第二個(gè)on Exception catch (e)
throw "Hello EeveyOne"; //只觸發(fā)第三個(gè)catch (e)
} on FormatException {
//捕獲具體的異常類型FormatException,沒有異常對(duì)象
print("I'm a OutOfLlamasException");
} on Exception catch (e) {
//只要是Exception類型的異常都能捕獲,并攜帶異常對(duì)象e
print('Unknown exception: $e');
} catch (e) {
// 任何throw拋出的類型都能捕獲
print('Something really unknown: $e');
}
}
如上面代碼所示,你可以使用on 或者 catch 來聲明捕獲語(yǔ)句,也可以 同時(shí)使用。使用 on 來指定異常類型,使用 catch 來 捕獲異常對(duì)象。
函數(shù) catch() 可以帶有一個(gè)或者兩個(gè)參數(shù), 第一個(gè)參數(shù)為拋出的異常對(duì)象, 第二個(gè)為堆棧信息 (一個(gè) StackTrace 對(duì)象)。
void main() {
try {
throw "Hello EeveyOne";
} on Exception catch (e) {
print('Unknown exception: $e');
} catch (e,s) { //e為異常對(duì)象,s為堆棧信息
print('Exception details:\n $e');
print('Stack trace:\n $s');
}
}

使用 rethrow 關(guān)鍵字可以 把捕獲的異常給 重新拋出,當(dāng)然也可以使用 throw拋出異常,不過使用rethrow更方便:
void misbehave() {
try {
dynamic foo = true;
print(foo++); // Runtime error
} catch (e) {
print('misbehave() partially handled ${e.runtimeType}.');
rethrow; // Allow callers to see the exception.
// throw e;等價(jià)于上面
}
}
void main() {
try {
misbehave();
} catch (e) {
print('main() finished handling ${e.runtimeType}.');
}
}
輸出:
misbehave() partially handled NoSuchMethodError.
main() finished handling NoSuchMethodError.
2.3、Finally
要確保某些代碼執(zhí)行,不管有沒有出現(xiàn)異常都需要執(zhí)行,可以使用 一個(gè) finally 語(yǔ)句來實(shí)現(xiàn)。<u>如果沒有 catch 語(yǔ)句來捕獲異常, 則在執(zhí)行完 finally 語(yǔ)句后, 異常被拋出了</u>:
void main() {
try {
throw "Hello EeveyOne";
} finally{
//finally執(zhí)行了,但是異常并沒有被捕獲,執(zhí)行后立馬拋出異常
print("My Name Finally");
}
print("End");//沒有而被執(zhí)行
}
打印如下:

finally子句在所有匹配到的catch子句之后運(yùn)行:
void main() {
try {
throw "Hello EeveyOne";
} catch (e){
print("Exception is $e");
} finally{
print("My Name Finally");
}
print("End");
}
輸出結(jié)果為:
Exception is Hello EeveyOne
My Name Finally
End
想了解更多請(qǐng)閱讀[Exceptions]章節(jié)。
歡迎關(guān)注我的公眾號(hào)【不喝咖啡的程序員】,最新的文章會(huì)在上面發(fā)布:
