文章目录

  • 可选参数
  • 命名可选参数
  • 位置可选参数
  • 默认参数值
  • main() 函数
  • 函数是一等对象
  • 匿名函数
  • 返回值

Dart 是一门真正面向对象的语言, 甚至其中的函数也是对象,并且有它的类型 Function 。 这也意味着函数可以被赋值给变量或者作为参数传递给其他函数。 也可以把 Dart 类的实例当做方法来调用。
已下是函数实现的示例:

bool isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

虽然在 Effective Dart 中推荐 公共API中声明类型, 但是省略了类型声明,函数依旧是可以正常使用的:

isNoble(atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

如果函数中只有一句表达式,可以使用简写语法:

bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
=> expr 语法是 { return expr; } 的简写。 => 符号 有时也被称为 箭头 语法。

提示: 在箭头 (=>) 和分号 ( ; ) 之间只能使用一个 表达式 ,不能是 语句 。 例如:不能使用 if 语句 ,但是可以是用 条件表达式.

函数有两种参数类型: required 和 optional。 required 类型参数在参数最前面, 随后是 optional 类型参数。 命名的可选参数也可以标记为 “@ required” 。 参考下一章节,了解更多细节。

可选参数

可选参数可以是命名参数或者位置参数,但一个参数只能选择其中一种方式修饰。

命名可选参数

调用函数时,可以使用指定命名参数paramName: value。 例如:


enableFlags(bold: true, hidden: false);

定义函数是,使用{param1, param2, …}来指定命名参数:


/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold, bool hidden}) {...}

Flutter 创建实例的表达式可能很复杂, 因此窗口小部件构造函数仅使用命名参数。 这样创建实例的表达式更易于阅读。

使用 @required注释表示参数是 required 性质的命名参数, 该方式可以在任何 Dart 代码中使用(不仅仅是Flutter)。


const Scrollbar({Key key, @required Widget child})

此时 Scrollbar 是一个构造函数, 当 child 参数缺少时,分析器会提示错误。

Required 被定义在meta package。 无论是直接引入(import) package:meta/meta.dart,或者引入了其他 package,而这个 package 输出(export)了 meta,比如 Flutter 的 package:flutter/material.dart。

位置可选参数

将参数放到[]中来标记参数是可选的:


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');
默认参数值

在定义方法的时候,可以使用 = 来定义可选参数的默认值。 默认值只能是编译时常量。 如果没有提供默认值,则默认值为 null。

下面是设置可选参数默认值示例:


/// 设置 [bold] 和 [hidden] 标志 ...
void enableFlags({bool bold = false, bool hidden = false}) {...}

// bold 值为 true; hidden 值为 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');

list 或 map 可以作为默认值传递。 下面的示例定义了一个方法 doStuff(), 并分别指定参数 list和 gifts 的默认值。


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() 函数

任何应用都必须有一个顶级 main()函数,作为应用服务的入口。 main() 函数返回值为空,参数为一个可选的 List<String>。

下面是 web 应用的 main()函数:


void main() {
  querySelector('#sample_text_id')
    ..text = 'Click me!'
    ..onClick.listen(reverseText);
}

提示:以上代码中的 … 语法为 级联调用 (cascade)。 使用级联调用, 可以简化在一个对象上执行的多个操作。

下面是一个命令行应用的main() 方法,并且使用了输入参数:


// 这样运行应用: 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 可以定义和解析命令行参数。

函数是一等对象

一个函数可以作为另一个函数的参数。 例如:


void printElement(int element) {
  print(element);
}

var list = [1, 2, 3];

// 将 printElement 函数作为参数传递。
list.forEach(printElement);

同样可以将一个函数赋值给一个变量,例如:


var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');

示例中使用了匿名函数。 下一章节会有更多介绍。

匿名函数

多数函数是有名字的, 比如 main()和 printElement()。 也可以创建没有名字的函数,这种函数被称为 匿名函数, 有时候也被称为 lambda 或者 closure 。 匿名函数可以赋值到一个变量中, 举个例子,在一个集合中可以添加或者删除一个匿名函数。

匿名函数和命名函数看起来类似— 在括号之间可以定义一些参数或可选参数,参数使用逗号分割。

后面大括号中的代码为函数体:


([[Type] param1[, …]]) {
  codeBlock;
};

下面例子中定义了一个包含一个无类型参数 item 的匿名函数。 list 中的每个元素都会调用这个函数,打印元素位置和值的字符串。


var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});

如果函数只有一条语句, 可以使用箭头简写。两个函数是等价的。


list.forEach(
    (item) => print('${list.indexOf(item)}: $item'));

返回值

所有函数都会返回一个值。 如果没有明确指定返回值, 函数体会被隐式的添加return null;语句。


foo() {}

assert(foo() == null);