Dart基礎之定義函數

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構造函數)僅命名參數,即便對於必需參數也是如此。測試

(可選參數)Optional parameters

可選參數能夠是命名參數,也能夠是位置參數,但不能同時知足ui

(命名參數)Named parameters

調用函數時,你可使用 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

(位置參數)Positional parameters

[]中包裝一組函數參數將它們標記爲可選的位置參數:

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');
}
複製代碼

main()函數

每一個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 參數。

Functions 做爲優秀的對象

你能夠把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 !!!');
複製代碼

這個例子用了一個匿名函數。更多關於匿名函數的知識,隨後分解。

匿名函數(Anonymous functions)

大對數的函數都是有名字的,例如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);
複製代碼
相關文章
相關標籤/搜索