一两蟀、函數(shù)參數(shù)
在Dart函數(shù)參數(shù)是一個(gè)比較重要的概念网梢,此外它涉及到概念的種類比較多,比如位置參數(shù)赂毯、命名參數(shù)战虏、可選位置參數(shù)、可選命名參數(shù)等等党涕。函數(shù)總是有一個(gè)所謂形參列表烦感,雖然這個(gè)參數(shù)列表可能為空,比如getter函數(shù)就是沒有參數(shù)列表的. 此外在Dart中函數(shù)參數(shù)大致可分為兩種: 位置參數(shù)和命名參數(shù)膛堤,來一張圖理清它們的概念關(guān)系
1手趣、位置參數(shù)
位置參數(shù)可以必需的也可以是可選。
- 無參數(shù)
//無參數(shù)類型-這是不帶函數(shù)參數(shù)或者說參數(shù)列表為空
String getDefaultErrorMsg() => 'Unknown Error!';
//無參數(shù)類型-等價(jià)于上面函數(shù)形式肥荔,同樣是參數(shù)列表為空
get getDefaultErrorMsg => 'Unknown Error!';
- 必需位置參數(shù)
//必需位置參數(shù)類型-這里的exception是必需的位置參數(shù)
String getErrorMsg(Exception exception) => exception.toString();
- 可選位置參數(shù)
//注意: 可選位置參數(shù)是中括號(hào)括起來表示绿渣,例如[String error]
String getErrorMsg([String error]) => error ?? 'Unknown Error!';
- 必需位置參數(shù)和可選位置參數(shù)混合
//注意: 可選位置參數(shù)必須在必需位置參數(shù)的后面
String getErrorMsg(Exception exception, [String extraInfo]) => '${exception.toString()}---$extraInfo';
2、命名參數(shù)
命名參數(shù)始終是可選參數(shù)燕耿。為什么是命名參數(shù)中符,這是因?yàn)樵谡{(diào)用函數(shù)時(shí)可以任意指定參數(shù)名來傳參。
- 可選命名參數(shù)
//注意: 可選命名參數(shù)是大括號(hào)括起來表示誉帅,例如{num a, num b, num c, num d}
num add({num a, num b, num c, num d}) {
return a + b + c + d;
}
//調(diào)用
main() {
print(add(d: 4, b: 3, a: 2, c: 1));//這里的命名參數(shù)就是可以任意順序指定參數(shù)名傳值,例如d: 4, b: 3, a: 2, c: 1
}
- 必需位置參數(shù)和可選命名參數(shù)混合
//注意: 可選命名參數(shù)必須在必需位置參數(shù)的后面
num add(num a, num b, {num c, num d}) {
return a + b + c + d;
}
//調(diào)用
main() {
print(add(4, 5, d: 3, c: 1));//這里的命名參數(shù)就是可以任意順序指定參數(shù)名傳值,例如d: 3, c: 1,但是必需參數(shù)必須按照順序傳參淀散。
}
- 注意: 可選位置參數(shù)和可選命名參數(shù)不能混合在一起使用,因?yàn)榭蛇x參數(shù)列表只能位于整個(gè)函數(shù)形參列表的最后蚜锨。
void add7([num a, num b], {num c, num d}) {//非法聲明档插,想想也沒有必要兩者一起混合使用場(chǎng)景。所以
...
}
3踏志、關(guān)于可選位置參數(shù)[num a, num b]
和可選命名參數(shù){num a, num b}
使用場(chǎng)景
可能問題來了阀捅,啥時(shí)候使用可選位置參數(shù),啥時(shí)候使用可選命名參數(shù)呢?
這里給個(gè)建議: 首先针余,參數(shù)是非必需的也就是可選的饲鄙,如果可選參數(shù)個(gè)數(shù)只有一個(gè)建議直接使用可選位置參數(shù)[num a, num b]
;如果可選參數(shù)個(gè)數(shù)是多個(gè)的話建議用可選命名參數(shù){num a, num b}
. 因?yàn)槎鄠€(gè)參數(shù)可選圆雁,指定參數(shù)名傳參對(duì)整體代碼可讀性有一定的增強(qiáng)忍级。
4、參數(shù)默認(rèn)值(針對(duì)可選參數(shù))
首先伪朽,需要明確一點(diǎn)轴咱,參數(shù)默認(rèn)值只針對(duì)可選參數(shù)才能添加的。可以使用 = 來定義命名和位置參數(shù)的默認(rèn)值朴肺。默認(rèn)值必須是編譯時(shí)常量窖剑。如果沒有提供默認(rèn)值,則默認(rèn)值為null戈稿。
- 可選位置參數(shù)默認(rèn)值
num add(num a, num b, num c, [num d = 5]}) {//使用=來賦值默認(rèn)值
return a + b + c + d;
}
main() {
print(add(1, 2, 3));//有默認(rèn)值參數(shù)可以省略不傳 實(shí)際上求和結(jié)果是: 1 + 2 + 3 + 5(默認(rèn)值)
print(add(1, 2, 3, 4));//有默認(rèn)值參數(shù)指定傳入4西土,會(huì)覆蓋默認(rèn)值,所以求和結(jié)果是: 1 + 2 + 3 + 4
}
- 可選命名參數(shù)默認(rèn)值
num add({num a, num b, num c = 3, num d = 4}) {
return a + b + c + d;
}
main() {
print(add(100, 100, d: 100, c: 100));
}
二鞍盗、匿名函數(shù)(閉包需了,lambda)
在Dart中可以創(chuàng)建一個(gè)沒有函數(shù)名稱的函數(shù),這種函數(shù)稱為匿名函數(shù)般甲,或者lambda函數(shù)或者閉包函數(shù)肋乍。但是和其他函數(shù)一樣,它也有形參列表敷存,可以有可選參數(shù)墓造。
(num x) => x;//沒有函數(shù)名,有必需的位置參數(shù)x
(num x) {return x;}//等價(jià)于上面形式
(int x, [int step]) => x + step;//沒有函數(shù)名历帚,有可選的位置參數(shù)step
(int x, {int step1, int step2}) => x + step1 + step2;////沒有函數(shù)名滔岳,有可選的命名參數(shù)step1、step2
閉包在dart中的應(yīng)用
閉包函數(shù)在dart用的特別多挽牢,單從集合中操作符來說就有很多谱煤。
main() {
List<int> numbers = [3, 1, 2, 7, 12, 2, 4];
//reduce函數(shù)實(shí)現(xiàn)累加,reduce函數(shù)中接收的(prev, curr) => prev + curr就是一個(gè)閉包
print(numbers.reduce((prev, curr) => prev + curr));
//還可以不用閉包形式來寫禽拔,但是這并不是一個(gè)好的方案,不建議下面這樣使用刘离。
plus(prev, curr) => prev + curr;
print(numbers.reduce(plus));
}
//reduce函數(shù)定義
E reduce(E combine(E value, E element)) {//combine閉包函數(shù)
Iterator<E> iterator = this.iterator;
if (!iterator.moveNext()) {
throw IterableElementError.noElement();
}
E value = iterator.current;
while (iterator.moveNext()) {
value = combine(value, iterator.current);//執(zhí)行combine函數(shù)
}
return value;
}
三、箭頭函數(shù)
在Dart中還有一種函數(shù)的簡(jiǎn)寫形式睹栖,那就是箭頭函數(shù)硫惕。箭頭函數(shù)是只能包含一行表達(dá)式的函數(shù),會(huì)注意到它沒有花括號(hào)野来,而是帶有箭頭的恼除。箭頭函數(shù)更有助于代碼的可讀性,類似于Kotlin或Java中的lambda表達(dá)式->
的寫法曼氛。
main() {
List<int> numbers = [3, 1, 2, 7, 12, 2, 4];
print(numbers.reduce((prev, curr) {//閉包簡(jiǎn)寫形式
return prev + curr;
}));
print(numbers.reduce((prev, curr) => prev + curr)); //等價(jià)于上述形式豁辉,箭頭函數(shù)簡(jiǎn)寫形式
}
四、局部函數(shù)
在Dart中還有一種可以直接定義在函數(shù)體內(nèi)部的函數(shù)舀患,可以把稱為局部函數(shù)或者內(nèi)嵌函數(shù)徽级。我們知道函數(shù)聲明可以出現(xiàn)頂層,比如常見的main函數(shù)等等聊浅。局部函數(shù)的好處就是從作用域角度來看餐抢,它可以訪問外部函數(shù)變量现使,并且還能避免引入一個(gè)額外的外部函數(shù),使得整個(gè)函數(shù)功能職責(zé)統(tǒng)一旷痕。
//定義外部函數(shù)fibonacci
int fibonacci(int n) {
//定義局部函數(shù)lastTwo
List<int> lastTwo(int n) {
if(n < 1) {
return <int>[0, 1];
} else {
var p = lastTwo(n - 1);
return <int>[p[1], p[0] + p[1]];
}
}
return lastTwo(n)[1];
}
五碳锈、頂層函數(shù)和靜態(tài)函數(shù)
在Dart中有一種特別的函數(shù),我們知道在面向?qū)ο笳Z言中比如Java苦蒿,并不能直接定義一個(gè)函數(shù)的殴胧,而是需要定義一個(gè)類,然后在類中定義函數(shù)佩迟。但是在Dart中可以不用在類中定義函數(shù),而是直接基于dart文件頂層定義函數(shù)竿屹,這種函數(shù)我們一般稱為頂層函數(shù)报强。最常見就是main函數(shù)了。而靜態(tài)函數(shù)就和Java中類似拱燃,依然使用static關(guān)鍵字來聲明秉溉,然后必須是定義在類的內(nèi)部的。
//頂層函數(shù)碗誉,不定義在類的內(nèi)部
main() {
print('hello dart');
}
class Number {
static int getValue() => 100;//static修飾定義在類的內(nèi)部召嘶。
}
六、main函數(shù)
每個(gè)應(yīng)用程序都有一個(gè)頂級(jí)的main()函數(shù)哮缺,它作為應(yīng)用程序的入口點(diǎn)弄跌。main()函數(shù)返回void,所以在dart可以直接省略void尝苇,并有一個(gè)可選的列表參數(shù)作為參數(shù)铛只。
//你一般看到的main是這樣的
main() {
print('hello dart');
}
//實(shí)際上它和Java類似可以帶個(gè)參數(shù)列表
main(List<String> args) {
print('hello dart: ${args[0]}, ${args[1]}');//用dart command執(zhí)行的時(shí)候: dart test.dart arg0 arg1 =>輸出:hello dart: arg0, arg1
}
七、Function函數(shù)對(duì)象
在Dart中一切都是對(duì)象糠溜,函數(shù)也不例外淳玩,函數(shù)可以作為一個(gè)參數(shù)傳遞。其中Function
類是代表所有函數(shù)的公共頂層接口抽象類非竿。Function
類中并沒有聲明任何實(shí)例方法蜕着。但是它有一個(gè)非常重要的靜態(tài)類函數(shù)apply
. 該函數(shù)接收一個(gè)Function
對(duì)象function
,一個(gè)List
的參數(shù)positionalArguments
以及一個(gè)可選參數(shù)Map<Symbol, dynamic>
類型的namedArguments
红柱。大家似乎明白了什么承匣?知道為啥dart中函數(shù)支持位置參數(shù)和命名參數(shù)嗎? 沒錯(cuò)就是它們兩個(gè)參數(shù)功勞。實(shí)際上豹芯,apply()函數(shù)提供一種使用動(dòng)態(tài)確定的參數(shù)列表來調(diào)用函數(shù)的機(jī)制悄雅,通過它我們就能處理在編譯時(shí)參數(shù)列表不確定的情況。
abstract class Function {
external static apply(Function function, List positionalArguments,
[Map<Symbol, dynamic> namedArguments]);//可以看到這是external聲明铁蹈,我們需要找到對(duì)應(yīng)的function_patch.dart實(shí)現(xiàn)
int get hashCode;
bool operator ==(Object other);
}
在sdk源碼中找到sdk/lib/_internal/vm/lib/function_patch.dart
對(duì)應(yīng)的function_patch
的實(shí)現(xiàn)
@patch
class Function {
// TODO(regis): Pass type arguments to generic functions. Wait for API spec.
//可以看到內(nèi)部私有的_apply函數(shù)宽闲,最終接收兩個(gè)List原生類型的參數(shù)arguments,names分別代表著我們使用函數(shù)時(shí)
//定義的所有參數(shù)List集合arguments(包括位置參數(shù)和命名參數(shù))以及命名參數(shù)名List集合names众眨,不過它是委托到native層的Function_apply C++函數(shù)實(shí)現(xiàn)的。
static _apply(List arguments, List names) native "Function_apply";
@patch
static apply(Function function, List positionalArguments,
[Map<Symbol, dynamic> namedArguments]) {
//計(jì)算外部函數(shù)位置參數(shù)的個(gè)數(shù)
int numPositionalArguments = 1 + // 默認(rèn)同時(shí)會(huì)傳入function參數(shù)容诬,所以默認(rèn)+1
(positionalArguments != null ? positionalArguments.length : 0);//位置參數(shù)的集合不為空就返回集合長(zhǎng)度否則返回0
//計(jì)算外部函數(shù)命名參數(shù)的個(gè)數(shù)
int numNamedArguments = namedArguments != null ? namedArguments.length : 0;;//命名參數(shù)的集合不為空就返回集合長(zhǎng)度否則返回0
//計(jì)算所有參數(shù)個(gè)數(shù)總和: 位置參數(shù)個(gè)數(shù) + 命名參數(shù)個(gè)數(shù)
int numArguments = numPositionalArguments + numNamedArguments;
//創(chuàng)建一個(gè)定長(zhǎng)為所有參數(shù)個(gè)數(shù)大小的List集合arguments
List arguments = new List(numArguments);
//集合第一個(gè)元素默認(rèn)是傳入的function對(duì)象
arguments[0] = function;
//然后從1的位置開始插入所有的位置參數(shù)到arguments參數(shù)列表中
arguments.setRange(1, numPositionalArguments, positionalArguments);
//然后再創(chuàng)建一個(gè)定長(zhǎng)為命名參數(shù)長(zhǎng)度的List集合
List names = new List(numNamedArguments);
int argumentIndex = numPositionalArguments;
int nameIndex = 0;
//遍歷命名參數(shù)Map集合
if (numNamedArguments > 0) {
namedArguments.forEach((name, value) {
arguments[argumentIndex++] = value;//把命名參數(shù)對(duì)象繼續(xù)插入到arguments集合中
names[nameIndex++] = internal.Symbol.getName(name);//并把對(duì)應(yīng)的參數(shù)名標(biāo)識(shí)存入names集合中
});
}
return _apply(arguments, names);//最后調(diào)用_apply函數(shù)傳入所有參數(shù)對(duì)象集合以及命名參數(shù)名稱集合
}
}
不妨再來瞅瞅C++層中的Function_apply
的實(shí)現(xiàn)
DEFINE_NATIVE_ENTRY(Function_apply, 0, 2) {
const int kTypeArgsLen = 0; // TODO(regis): Add support for generic function.
const Array& fun_arguments =
Array::CheckedHandle(zone, arguments->NativeArgAt(0));//獲取函數(shù)的所有參數(shù)對(duì)象數(shù)組 fun_arguments
const Array& fun_arg_names =
Array::CheckedHandle(zone, arguments->NativeArgAt(1));//獲取函數(shù)的命名參數(shù)參數(shù)名數(shù)組 fun_arg_names
const Array& fun_args_desc = Array::Handle(
zone, ArgumentsDescriptor::New(kTypeArgsLen, fun_arguments.Length(),
fun_arg_names));//利用 fun_arg_names生成對(duì)應(yīng)命名參數(shù)描述符集合
//注意: 這里會(huì)調(diào)用DartEntry中的InvokeClosure函數(shù)娩梨,傳入了所有參數(shù)對(duì)象數(shù)組 fun_arguments和fun_arg_names生成對(duì)應(yīng)命名參數(shù)描述符集合
//最后返回result
const Object& result = Object::Handle(
zone, DartEntry::InvokeClosure(fun_arguments, fun_args_desc));
if (result.IsError()) {
Exceptions::PropagateError(Error::Cast(result));
}
return result.raw();
}
總結(jié)
到這里有關(guān)Dart中的函數(shù)就說完,有了這篇文章相信大家對(duì)dart函數(shù)應(yīng)該有個(gè)全面的了解了览徒。歡迎持續(xù)狈定,下一篇我們將進(jìn)入Dart中的面向?qū)ο?..