第126期:Dart基础知识(函数)

1,660 阅读2分钟

Functions 函数

Dart是一种面向对象语言,所以即使函数也是对象,也有一个类型Function。这意味着函数可以分配给变量或作为参数传递给其他函数。我们还可以像调用函数一样调用Dart类的实例。

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

尽管Dart建议为公共API添加类型注释,但如果省略类型,该函数仍然有效:

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

和JS类似,如果函数只有一个表达式,我们可以使用短语法箭头函数

bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

Params 参数

函数可以具有任意数量的参数。这些参数后面可以是命名参数,也可以是可选的位置参数(但不能同时)。

一些API,尤其是Flutter小部件构造函数只使用具名参数

具名参数

定义函数是指定具体的参数,具名参数是可选的

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

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

enableFlags(bold: true, hidden: false);

虽然具名参数是可选的,我们可以用require来指定这个参数是必须的:

const Scrollbar({super.key, required Widget child});

当我们将参数用[]标记为可选参数:

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

/// 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']) {
  var result = '$from says $msg with a $device';
  return result;
}

assert(say('Bob', 'Howdy') == 'Bob says Howdy with a carrier pigeon');

当然,我们也可以指定LIST,MAP的默认值:

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()函数没有返回值,表示的是程序的开始。

void main() {
  print('Hello, World!');
}

匿名函数

大多数函数都是具名的,例如main()或printElement()。我们也可以创建一个名为匿名函数的无名函数,有时也可以创建lambda或闭包。我们可以将匿名函数分配给变量,比如我们可以在集合中进行添加或删除操作。

匿名函数看起来类似于具名函数,括号之间用逗号和可选类型注释分隔零个或多个参数。

const list = ['apples', 'bananas', 'oranges'];
list.map((item) {
  return item.toUpperCase();
}).forEach((item) {
  print('$item: ${item.length}');
});

这里的:

(item) {
  print('$item: ${item.length}');
}

就是匿名函数。

Lexical scope 词域

在JS中,我们通常称为作用域。

下面的例子可以简单看下,理解一下就行了。

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);
    }
  }
}

Lexical closures 闭包

闭包是一个函数对象,它可以访问其词法范围内的变量,即使函数在其原始范围外使用。

/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(int addBy) {
  return (int 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);
}

makeAdder实际上返回了一个函数,所以变量add2包含的是一个函数的引用,然后才调用的add2,add4也一样。

Return Values 返回值

所有函数都返回一个值。如果未指定返回值,则语句返回null;隐式附加到函数体。

foo() {}

assert(foo() == null);