【Flutter】Dart語法篇之函數(shù)的使用(四)

一两蟀、函數(shù)參數(shù)

在Dart函數(shù)參數(shù)是一個(gè)比較重要的概念网梢,此外它涉及到概念的種類比較多,比如位置參數(shù)赂毯、命名參數(shù)战虏、可選位置參數(shù)、可選命名參數(shù)等等党涕。函數(shù)總是有一個(gè)所謂形參列表烦感,雖然這個(gè)參數(shù)列表可能為空,比如getter函數(shù)就是沒有參數(shù)列表的. 此外在Dart中函數(shù)參數(shù)大致可分為兩種: 位置參數(shù)和命名參數(shù)膛堤,來一張圖理清它們的概念關(guān)系

image

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ū)ο?..

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市习蓬,隨后出現(xiàn)的幾起案子纽什,更是在濱河造成了極大的恐慌,老刑警劉巖躲叼,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芦缰,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡枫慷,警方通過查閱死者的電腦和手機(jī)让蕾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來或听,“玉大人探孝,你說我怎么就攤上這事∮桑” “怎么了顿颅?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)找御。 經(jīng)常有香客問我元镀,道長(zhǎng),這世上最難降的妖魔是什么霎桅? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任栖疑,我火速辦了婚禮,結(jié)果婚禮上滔驶,老公的妹妹穿的比我還像新娘遇革。我一直安慰自己,他們只是感情好揭糕,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布萝快。 她就那樣靜靜地躺著,像睡著了一般著角。 火紅的嫁衣襯著肌膚如雪揪漩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天吏口,我揣著相機(jī)與錄音奄容,去河邊找鬼冰更。 笑死,一個(gè)胖子當(dāng)著我的面吹牛昂勒,可吹牛的內(nèi)容都是我干的蜀细。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼戈盈,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼奠衔!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起塘娶,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤归斤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后血柳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體官册,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年难捌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸦难。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡根吁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出合蔽,到底是詐尸還是另有隱情击敌,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布拴事,位于F島的核電站沃斤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏刃宵。R本人自食惡果不足惜衡瓶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望牲证。 院中可真熱鬧哮针,春花似錦、人聲如沸坦袍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捂齐。三九已至蛮放,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奠宜,已是汗流浹背包颁。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工瞻想, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人徘六。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓内边,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親待锈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子漠其,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容