Dart 语言系列
Functions
Dart
是一个真正面向对象的语言,因此即使是函数也是对象并且有类型 Function
,这就意味着可以赋值给变量或者被当做参数传递给其他函数。你可以调用Dart
类的实例即使他是函数。更多细节,参考 Callable classes.
这里有一个函数实现的例子:
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
虽然高效的Dart
推荐 标明类型对于公开的APIs,函数仍然可以执行即使你省略了类型:
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
对于仅仅包含一个表达式的函数,你可以使用速记的语法:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
=> expr
语法是一个针对{ return expr; }
的简写。=>
这种记号
被当做箭头函数。
Note:仅仅是表达式- 而不是语句 - 可以出现在(=>
) 箭头 和分号(;
)之间。例如:你不能这个里使用if
语句,但是你可以使用条件表达式.
一个函数可以拥有两种类型的参数:required
和 optional
.required
的参数首先被列出来,接下来是optional
参数。命名为optional
参数也可以被标记为 @required
.
Optional parameters
当调用一个函数,你可以指定命名的参数使用paramName: value
,例如:
enableFlags(bold: true, hidden: false);
当定义一个函数,使用{param1, param2, …}
来指定命名的参数:
/// Sets the [bold] and [hidden] flags ...//设置[bold]和[hidden]标志...
void enableFlags({bool bold, bool hidden}) {...}
Flutter
实例创建的表达式可能会复杂,因此widget
构造仅仅使用命名参数。这样可以使实例创建的表达式更易于阅读。
你可以标注一个命名的参数在任何Dart
代码中(不仅仅是Flutter
)是@required
来注明它是一个必须的参数.例如:
const Scrollbar({Key key, @required Widget child})
当 一个Scrollbar
构造时,编译器会报告一个错误当子参数不存在。
Required
被定义在 meta
package中。要不直接引入package:meta/meta.dart
,或者引入另一个已经包含meta
的package,例如Flutter’s
中 package:flu tter/material.dart
.
Optional 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');
Default parameter values
你的函数可以使用 =
来定义一个默认值给 命名的和可选的参数。默认的值必须是编译期常量.如果没有默认值可以提供,默认值就是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);
Deprecation note:旧的代码可能使用冒号(:
) 而不是 =
来给命名参数设置默认值。原因是最初:
只支持命名参数。这个支持已经被废弃,因此推荐使用 =
指定默认值.
下面例子将展示如何给位置参数设置默认值:
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
当做默认值.下面的例子定义一个函数doStuff()
,将使用默认的list 当做list
参数 和一个默认的map 参数当做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');
}
The main() function
每一个app 必须有一个顶级的 main()
函数,这被当做app 的入口。
main()
函数返回是void
并且有一个可选List<String>
参数。
这里是一个web app 的main()
函数的例子:
void main() {
querySelector('#sample_text_id')
..text = 'Click me!'
..onClick.listen(reverseText);
}
Note:..
语法在前面的代码中称作 cascade . 使用 cascades
,您可以对单个对象的成员执行多个操作
这里有命令行app带有参数 关于 main()
函数的例子:
void main(List<String> arguments) {
print(arguments);
assert(arguments.length == 2);
assert(int.parse(arguments[0]) == 1);
assert(arguments[1] == 'test');
}
你可以使用 [args library](https://pub.dartlang.org/packages/args)
定义和解析命令行参数。
Functions as first-class objects
你可以传递一个函数当做参数给另一个函数。例如:
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
// Pass printElement as a parameter.
list.forEach(printElement);
你也可以吧一个函数赋值给变量,例如:
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
这个例子使用了匿名函数。
Anonymous functions
大多数函数是命名的,例如 main()
或 printElement()
.你可以创建一个没有名字的函数称作 anonymous function(匿名函数),或者有时候是lambda
或者 closure
.你可以将一个匿名函数赋值给一个变量,例如从一个集合中添加它或者移除它。
一个匿名函数同命名函数是类似的 - 零个或者多个参数,在逗号和括号之间用逗号和可选类型注释分隔。
下面代码block
包含 函数体:
([[Type] param1[, …]]) {
codeBlock;
};
下面的例子定义了一个未定义类型参数item
的匿名函数,在函数中被执行,将会打印出字符串指定的index
的值。
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
print('${list.indexOf(item)}: $item');
});
如果一个函数仅仅包含一个语句,你可以使用箭头标注来简写。赋值下面代码在 DartPad
,并且点击运行来验证函数功能是相等的。
list.forEach(
(item) => print('${list.indexOf(item)}: $item'));
Lexical scope
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 ()
函数如何使用变量从每个层级,所有的都可以访问顶级。
Lexical closures
闭包是一个可以在它作用域访问变量的函数对象,甚至可以访问它初始函数范围之外。
Functions
可以关闭定义在周围作用域的变量。在下面的例子中,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);
}
Testing functions for equality
这里是一个验证顶级函数,静态方法和实例方法相等的例子:
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);
}
注:上述例子很容易理解,同一对象的方法是相等的,不同对象的方法是不等的,因为内存空间分布是不同的.
Return values
所有的函数都会返回一个值。如果没有指明返回值,表达式返回的是null
;隐式附加到函数体。
foo() {}
assert(foo() == null);