- Dart 开发语言概览
- 一个简单的 Dart 程序
- 重要概念
- 关键字
- 变量
- 内置类型
- Functions
- 运算符Dart defines the operators shown in the following table.You can override many of these operators, as described inOverridable operators.
- 关系运算符下表列出了关系运算符及含义:
- 类型判断运算符as、is、is! 运算符是在运行时判断对象类型的运算符。
- 按位和移位运算符在 Dart 中,二进制位运算符可以操作二进制的某一位,但仅适用于整数。
- 条件表达式Dart 有两个特殊的运算符可以用来替代 if-else 语句:- condition ? expr1 : expr2- If condition is true, evaluates expr1 (and returns its value);otherwise, evaluates and returns the value of expr2.条件 ? 表达式 1 : 表达式 2:如果条件为 true,执行表达式 1并返回执行结果,否则执行表达式 2 并返回执行结果。- expr1 ?? expr2- If expr1 is non-null, returns its value;otherwise, evaluates and returns the value of expr2.表达式 1 ?? 表达式 2:如果表达式 1 为非 null 则返回其值,否则执行表达式 2 并返回其值。如果赋值是根据布尔表达式则考虑使用 ?:。
- 级联运算符(..)级联运算符(..)可以让你在同一个对象上连续调用多个对象的变量或方法。比如下面的代码:
- While 和 Do-Whilewhile 循环会在执行循环体前先判断条件:
- Break 和 Continue使用 break 可以中断循环:
- Switch 和 CaseSwitch 语句在 Dart 中使用 == 来比较整数、字符串或编译时常量,比较的两个对象必须是同一个类型且不能是子类并且没有重写 == 操作符。枚举类型非常适合在 Switch 语句中使用。备忘:Dart 中的 Switch 语句仅适用于有限的情况,比如使用解释器和扫描器的场景。每一个非空的 case 子句都必须有一个 break 语句,也可以通过 continue、throw 或者 return 来结束非空 case 语句。当没有 case 语句匹配时,可以使用 default 子句来匹配这种情况:
- 捕获异常捕获异常可以避免异常继续传递(重新抛出异常除外)。捕获一个异常可以给你处理它的机会:
- Finally可以使用 finally 语句来包裹确保不管有没有异常都执行代码,如果没有指定 catch 语句来捕获异常,则在执行完 finally 语句后再抛出异常:
Dart 开发语言概览
本文将从变量和运算符开始到类和库的使用来向你介绍 Dart 编程语言的主要功能,这里假设你已经有使用其它语言进行编程的经验。
你可以通过查看 Dart 库概览 学习更多关于 Dart 核心库的知识。若还想了解更多有关语言功能的详细内容,请参阅 Dart 编程语言规范。
备忘:
使用 DartPad 可以体验 Dart 的大部分语言功能 (了解更多), 打开 DartPad
本页面内嵌了一些 DartPads 做例子展示,
如果你只看到了空白的框框(而没有任何内容),请查阅DartPad 常见问题页面。
一个简单的 Dart 程序
下面的应用程序代码用到了很多 Dart 的基本功能:
1. // Define a function.
2. printInteger(int aNumber) {
3. print('The number is $aNumber.'); // 打印输出到控制台。
4. }
6. // Dart 程序从 main() 函数开始执行。
7. main() {
8. var number = 42; // 声明并初始化一个变量。
9. printInteger(number); // 调用一个函数。
10. }
下面是上述应用程序中使用到的代码片段,这些代码片段适用于所有(或几乎所有)的 Dart 应用:
// This is a comment.
// 注释。- 以双斜杠开头的一行语句称为单行注释。Dart 同样支持多行注释和文档注释。查阅注释获取更多相关信息。
int- A type. Some of the other built-in typesare
String,List, andbool.表示一种数据类型。Dart 中一些其他的内置类型包括String、List和bool。 42- A number literal. Number literals are a kind of compile-time constant.表示一个数字字面量。数字字面量是一种编译时常量。
print()- A handy way to display output.一种便利的将信息输出显示的方式。
'…'(或"…")- 表示字符串字面量。
$variableName(或${expression})- 表示字符串插值:字符串字面量中包含的变量或表达式。查阅字符串获取更多相关信息。
main()- The special, required, top-level function where app executionstarts. For more information, seeThe main() function.一个特殊且 必须的 顶级函数,Dart 应用程序总是会从该函数开始执行。查阅 main() 函数 获取更多相关信息。
var- A way to declare a variable without specifying its type.用于定义变量,通过这种方式定义变量不需要指定变量类型。
备忘:
本站的代码遵循 Dart 风格指南 中的约定。
重要概念
当你在学习 Dart 语言时, 应该牢记以下几点:
- 所有变量引用的都是 对象,每个对象都是一个 类 的实例。数字、函数以及
null都是对象。所有的类都继承于 Object 类。 - 尽管 Dart 是强类型语言,但是在声明变量时指定类型是可选的,因为 Dart 可以进行类型推断。在上述代码中,变量
number的类型被推断为int类型。如果想显式地声明一个不确定的类型,可以使用特殊类型dynamic。 - Dart 支持泛型,比如
List<int>(表示一组由 int 对象组成的列表)或List<dynamic>(表示一组由任何类型对象组成的列表)。 - Dart 支持顶级函数(例如
main方法),同时还支持定义属于类或对象的函数(即 静态 和 实例方法)。你还可以在函数中定义函数(嵌套 或 局部函数)。 - Dart 支持顶级 变量,以及定义属于类或对象的变量(静态和实例变量)。实例变量有时称之为域或属性。
- Dart 没有类似于 Java 那样的
public、protected和private成员访问限定符。如果一个标识符以下划线 (_) 开头则表示该标识符在库内是私有的。可以查阅 库和可见性 获取更多相关信息。 - 标识符 可以以字母或者下划线 (_) 开头,其后可跟字符和数字的组合。
- Dart 中 表达式 和 语句 是有区别的,表达式有值而语句没有。比如条件表达式
expression condition ? expr1 : expr2中含有值expr1或expr2。与 if-else 分支语句相比,if-else分支语句则没有值。一个语句通常包含一个或多个表达式,但是一个表达式不能只包含一个语句。 - Dart 工具可以显示 警告 和 错误 两种类型的问题。警告表明代码可能有问题但不会阻止其运行。错误分为编译时错误和运行时错误;编译时错误代码无法运行;运行时错误会在代码运行时导致异常。
关键字
下面的表格中列出了 Dart 语言所使用的关键字。
| abstract 2 | dynamic 2 | implements 2 | show 1 |
| as 2 | else | import 2 | static 2 |
| assert | enum | in | super |
| async 1 | export 2 | interface 2 | switch |
| await 3 | extends | is | sync 1 |
| break | external 2 | library 2 | this |
| case | factory 2 | mixin 2 | throw |
| catch | false | new | true |
| class | final | null | try |
| const | finally | on 1 | typedef 2 |
| continue | for | operator 2 | var |
| covariant 2 | Function 2 | part 2 | void |
| default | get 2 | rethrow | while |
| deferred 2 | hide 1 | return | with |
| do | if | set 2 | yield 3 |
应该避免使用这些单词作为标识符。但是,带有上标的单词可以在必要的情况下作为标识符:
- 带有上标 1 的关键字为 上下文关键字,只有在特定的场景才有意义,它们可以在任何地方作为有效的标识符。
- 带有上标 2 的关键字为 内置标识符,其作用只是在JavaScript代码转为Dart代码时更简单,这些关键字在大多数时候都可以作为有效的标识符,但是它们不能用作类名或者类型名或者作为导入前缀使用。
- 带有上标 3 的关键字为 Dart1.0 发布后用于支持异步相关的特性新加的。不能在由关键字
async、async或sync标识的方法体中使用await或yield作为标识符。
其它没有上标的关键字为 保留字,均不能用作标识符。
变量
下面的示例代码将创建一个变量并将其初始化:
1. var name = 'Bob';
变量仅存储对象的引用。这里名为 name 的变量存储了一个 String 类型对象的引用,“Bob” 则是该对象的值。
name 变量的类型被推断为 String,但是你可以为其指定类型。如果一个对象的引用不局限于单一的类型,可以根据设计指南将其指定为 Object 或 dynamic 类型。
1. dynamic name = 'Bob';
除此之外你也可以指定类型:
1. String name = 'Bob';
备忘:
本文遵循 风格建议指南 中的建议,通过 var 声明局部变量而非使用指定的类型。
默认值
在 Dart 中,未初始化的变量拥有一个默认的初始化值:null。即便数字也是如此,因为在 Dart 中一切皆为对象,数字也不例外。
1. int lineCount;
2. assert(lineCount == null);
备忘:
assert() 的调用将会在生产环境的代码中被忽略掉。在开发过程中,assert(condition) 将会在 条件判断 为 false 时抛出一个异常。详情请查阅 Assert。
Final 和 Const
如果你不想更改一个变量,可以使用关键字 final 或者 const 修饰变量,这两个关键字可以替代 var 关键字或者加在一个具体的类型前。一个 final 变量只可以被赋值一次;一个 const 变量是一个编译时常量(const 变量同时也是 final 的)。顶层的 final 变量或者类的 final 变量在其第一次使用的时候被初始化。
备忘:
实例变量可以是 final 的但不可以是 const 的, final 实例变量必须在构造器开始前被初始化,比如在声明实例变量时初始化,或者作为构造器参数,或者将其置于构造器的 初始化列表中。
下面的示例中我们创建并设置两个 final 变量:
1. final name = 'Bob'; // Without a type annotation
2. final String nickname = 'Bobby';
你不能修改一个 final 变量的值:
1. name = 'Alice'; // Error: a final variable can only be set once.
使用关键字 const 修饰变量表示该变量为 编译时常量。如果使用 const 修饰类中的变量,则必须加上 static 关键字,即 static const(注意:顺序不能颠倒(译者注))。在声明 const 变量时可以直接为其赋值,也可以使用其它的 const 变量为其赋值:
1. const bar = 1000000; // 直接赋值 [Unit of pressure (dynes/cm2)]
2. const double atm = 1.01325 * bar; // 利用其它 const 变量赋值 (Standard atmosphere)
const 关键字不仅仅可以用来定义常量,还可以用来创建 常量值,该常量值可以赋予给任何变量。你也可以将构造函数声明为 const 的,这种类型的构造函数创建的对象是不可改变的。
1. var foo = const [];
2. final bar = const [];
3. const baz = []; // 相当于 `const []` (Equivalent to `const []`)
如果使用初始化表达式为常量赋值可以省略掉关键字 const,比如上面的常量 baz 的赋值就省略掉了 const。详情请查阅DON’T use const redundantly
没有使用 final 或 const 修饰的变量的值是可以被更改的,即使这些变量之前引用过 const 的值。
1. foo = [1, 2, 3]; // foo 的值之前为 const [] (Was const [])
常量的值不可以被修改:
1. baz = [42]; // 报错:常量不可以被赋值。(Error: Constant variables can't be assigned a value.)
As of Dart 2.5, you can define constants that usetype checks and casts (is and as),collection if and collection for,and spread operators (… and …?):
1. // Valid compile-time constants as of Dart 2.5.
2. const Object i = 3; // Where i is a const Object with an int value...
3. const list = [i as int]; // Use a typecast.
4. const map = {if (i is int) i: "int"}; // Use is and collection if.
5. const set = {if (list is List<int>) ...list}; // ...and a spread.
可以查阅 Lists、Maps 和 Classes 获取更多关于使用 const 创建常量值的信息。
内置类型
Dart 语言支持下列的类型:
- numbers
- strings
- booleans
- lists (也被称为 arrays)
- sets
- maps
- runes (用于在字符串中表示 Unicode 字符)
- symbols
可以直接使用字面量来初始化上述类型。例如 'This is a string' 是一个字符串字面量,true 是一个布尔字面量。
由于 Dart 中每个变量引用都指向一个对象(一个 类 的实例),你通常也可以使用 构造器 来初始化变量。一些内置的类型有它们自己的构造器。例如你可以使用 Map() 来创建一个 map 对象。
Numbers
Dart 支持两种 Number 类型:
- int
- 整数值;长度不超过 64位,具体取值范围依赖于不同的平台。在 DartVM 上其取值位于 -263 至 263 - 1 之间。编译成 JavaScript 的 Dart 使用 JavaScript 数字,其允许的取值范围在 -253 至 253 - 1 之间。
- double
- 64位的双精度浮点数字,且符合 IEEE 754 标准。
int 和 double 都是 num 的子类。num 中定义了一些基本的运算符比如 +、-、*、/ 等,还定义了 abs()、ceil() 和 floor() 等方法(位运算符,比如 » 定义在 int 中)。如果 num 及其子类不满足你的要求,可以查看 dart:math 库中的 API。
整数是不带小数点的数字,下面是一些定义整数字面量的例子:
1. var x = 1;
2. var hex = 0xDEADBEEF;
如果一个数字包含了小数点,那么它就是浮点型的。下面是一些定义浮点数字面量的例子:
1. var y = 1.1;
2. var exponents = 1.42e5;
从 Dart 2.1 开始,整型字面量将会在必要的时候自动转换成浮点数字面量:
1. double z = 1; // Equivalent to double z = 1.0.
版本提示:
在 Dart 2.1 之前,在浮点数上下文中使用整数字面量是错误的。
下面是字符串和数字之间转换的方式:
1. // String -> int
2. var one = int.parse('1');
3. assert(one == 1);
5. // String -> double
6. var onePointOne = double.parse('1.1');
7. assert(onePointOne == 1.1);
9. // int -> String
10. String oneAsString = 1.toString();
11. assert(oneAsString == '1');
13. // double -> String
14. String piAsString = 3.14159.toStringAsFixed(2);
15. assert(piAsString == '3.14');
The int type specifies the traditional bitwise shift (<<, >>), AND(&), and OR (|) operators. For example:
| 整型支持传统的位移操作,比如移位(<<、>>)、按位与(&)、按位或( | ),例如: |
1. assert((3 << 1) == 6); // 0011 << 1 == 0110
2. assert((3 >> 1) == 1); // 0011 >> 1 == 0001
3. assert((3 | 4) == 7); // 0011 | 0100 == 0111
数字字面量为编译时常量。很多算术表达式只要其操作数是常量,则表达式结果也是编译时常量。
1. const msPerSecond = 1000;
2. const secondsUntilRetry = 5;
3. const msUntilRetry = secondsUntilRetry * msPerSecond;
Strings
Dart 字符串是 UTF-16 编码的字符序列。可以使用单引号或者双引号来创建字符串:
1. var s1 = 'Single quotes work well for string literals.';
2. var s2 = "Double quotes work just as well.";
3. var s3 = 'It\'s easy to escape the string delimiter.';
4. var s4 = "It's even easier to use the other delimiter.";
6. // 代码中文解释
7. var s1 = '使用单引号创建字符串字面量。';
8. var s2 = "双引号也可以用于创建字符串字面量。";
9. var s3 = '使用单引号创建字符串时可以使用斜杠来转义那些与单引号冲突的字符串:\'。';
10. var s4 = "而在双引号中则不需要使用转义与单引号冲突的字符串:'";
可以在字符串中以 ${表达式} 的形式使用表达式,如果表达式是一个标识符,可以省略掉 {}。如果表达式的结果为一个对象,则 Dart 会调用该对象的 toString 方法来获取一个字符串。
1. var s = 'string interpolation';
3. assert('Dart has $s, which is very handy.' ==
4. 'Dart has string interpolation, ' +
5. 'which is very handy.');
6. assert('That deserves all caps. ' +
7. '${s.toUpperCase()} is very handy!' ==
8. 'That deserves all caps. ' +
9. 'STRING INTERPOLATION is very handy!');
11. // 代码中文解释
12. var s = '字符串插值';
14. assert('Dart 有$s,使用起来非常方便。' == 'Dart 有字符串插值,使用起来非常方便。');
15. assert('使用${s.substring(3,5)}表达式也非常方便' == '使用插值表达式也非常方便。');
备忘:
== 运算符判断两个对象的内容是否一样,如果两个字符串包含一样的字符编码序列,则表示相等。
可以使用 + 运算符将两个字符串连接为一个,也可以将多个字符串挨着放一起变为一个:
1. var s1 = 'String '
2. 'concatenation'
3. " works even over line breaks.";
4. assert(s1 ==
5. 'String concatenation works even over '
6. 'line breaks.');
8. var s2 = 'The + operator ' + 'works, as well.';
9. assert(s2 == 'The + operator works, as well.');
11. // 代码中文解释
12. var s1 = '可以拼接'
13. '字符串'
14. "即便它们不在同一行。";
15. assert(s1 == '可以拼接字符串即便它们不在同一行。');
17. var s2 = '使用加号 + 运算符' + '也可以达到相同的效果。';
18. assert(s2 == '使用加号 + 运算符也可以达到相同的效果。');
可以使用三个单引号或者三个双引号创建多行字符串:
1. var s1 = '''
2. 你可以像这样创建多行字符串。
3. ''';
5. var s2 = """这也是一个多行字符串。""";
在字符串前加上 r 作为前缀创建 “raw” 字符串(即不会被做任何处理(比如转义)的字符串):
1. var s = r'In a raw string, not even \n gets special treatment.';
3. // 代码中文解释
4. var s = r'在 raw 字符串中,转义字符串 \n 会直接输出 “\n” 而不是转义为换行。';
你可以查阅 Runes and grapheme clusters 获取更多关于如何在字符串中表示 Unicode 字符的信息。
字符串字面量是一个编译时常量,只要是编译时常量都可以作为字符串字面量的插值表达式:
1. // 可以将下面三个常量作为字符串插值拼接到字符串字面量中。(These work in a const string.)
2. const aConstNum = 0;
3. const aConstBool = true;
4. const aConstString = 'a constant string';
6. // 而下面三个常量则不能作为字符串插值拼接到字符串字面量。
7. var aNum = 0;
8. var aBool = true;
9. var aString = 'a string';
10. const aConstList = [1, 2, 3];
12. const validConstString = '$aConstNum $aConstBool $aConstString';
13. // const invalidConstString = '$aNum $aBool $aString $aConstList';
可以查阅 字符串和正则表达式 获取更多关于如何使用字符串的信息。
Booleans
Dart 使用 bool 关键字表示布尔类型,布尔类型只有两个对象 true 和 false,两者都是编译时常量。
Dart 的类型安全不允许你使用类似 if (nonbooleanValue) 或者 assert (nonbooleanValue) 这样的代码检查布尔值。相反,你应该总是显示地检查布尔值,比如像下面的代码这样:
1. // 检查是否为空字符串 (Check for an empty string).
2. var fullName = '';
3. assert(fullName.isEmpty);
5. // 检查是否小于等于零。
6. var hitPoints = 0;
7. assert(hitPoints <= 0);
9. // 检查是否为 null。
10. var unicorn;
11. assert(unicorn == null);
13. // 检查是否为 NaN。
14. var iMeantToDoThis = 0 / 0;
15. assert(iMeantToDoThis.isNaN);
Lists
数组 Array 是几乎所有编程语言中最常见的集合类型,在 Dart 中数组由 List 对象表示。通常称之为 List。
Dart 中 List 字面量看起来与 JavaScript 中数组字面量一样。下面是一个 Dart List 的示例:
1. var list = [1, 2, 3];
备忘:
这里 Dart 推断出 list 的类型为 List<int>,如果往该数组中添加一个非 int 类型的对象则会报错。你可以阅读 类型推断 获取更多相关信息。
List 的下标索引从 0 开始,第一个元素的下标为 0,最后一个元素的下标为 list.length - 1。你可以像 JavaScript 中的用法那样获取 Dart 中 List 的长度以及元素:
1. var list = [1, 2, 3];
2. assert(list.length == 3);
3. assert(list[1] == 2);
5. list[1] = 1;
6. assert(list[1] == 1);
如果想要创建一个编译时常量的 List,在 List 字面量前添加 const 关键字即可:
1. var constantList = const [1, 2, 3];
2. // constantList[1] = 1; // 取消注释将导致出错 (Uncommenting this causes an error.)
Dart 在 2.3 引入了 扩展操作符(…)和 null-aware 扩展操作符(…?),它们提供了一种将多个元素插入集合的简洁方法。
例如,你可以使用扩展操作符(…)将一个 List 中的所有元素插入到另一个 List 中:
1. var list = [1, 2, 3];
2. var list2 = [0, ...list];
3. assert(list2.length == 4);
如果扩展操作符右边可能为 null ,你可以使用 null-aware 扩展操作符(…?)来避免产生异常:
1. var list;
2. var list2 = [0, ...?list];
3. assert(list2.length == 1);
可以查阅扩展操作符建议获取更多关于如何使用扩展操作符的信息。
Dart 在 2.3 还同时引入了 Collection If 和 Collection For,在构建集合时,可以使用条件判断(if)和循环(for)。
下面示例是使用 Collection If 来创建一个 List 的示例,它可能包含 3 个或 4 个元素:
1. var nav = [
2. 'Home',
3. 'Furniture',
4. 'Plants',
5. if (promoActive) 'Outlet'
6. ];
下面示例是使用 Collection For 将列表中的元素修改后添加到另一个列表中的示例:
1. var listOfInts = [1, 2, 3];
2. var listOfStrings = [
3. '#0',
4. for (var i in listOfInts) '#$i'
5. ];
6. assert(listOfStrings[1] == '#1');
你可以查阅集合中使用控制流建议获取更多关于使用 Collection If 和 Collection For 的细节内容和示例。
List 类中有许多用于操作 List 的便捷方法,你可以查阅泛型和集合获取更多与之相关的信息。
Sets
A set in Dart is an unordered collection of unique items.Dart support for sets is provided by set literals and the Set type.
版本提示:
尽管 Set 类型(type) 一直都是 Dart 的一项核心功能,但是 Set 字面量(literals) 却是在 Dart2.2 中才加入的。
下面是使用 Set 字面量来创建一个 Set 集合的方法:
1. var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
备忘:
Dart 推断 halogens 变量是一个 Set<String> 类型的集合,如果往该 Set 中添加类型不正确的对象则会报错。你可以查阅 类型推断 获取更多与之相关的内容。
可以使用在 {} 前加上类型参数的方式创建一个空的 Set,或者将 {} 赋值给一个 Set 类型的变量:
1. var names = <String>{}; // 类型+{}的形式创建Set。
2. // Set<String> names = {}; // 声明类型变量的形式创建 Set (This works, too).
3. // var names = {}; // 这样的形式将创建一个 Map 而不是 Set (Creates a map, not a set.)
Set 还是 map? Map 字面量语法同 Set 字面量语法非常相似。因为先有的 Map 字面量语法,所以 {} 默认是 Map 类型。如果忘记在 {} 上注释类型或赋值到一个未声明类型的变量上,那么 Dart 会创建一个类型为 Map<dynamic, dynamic> 的对象。
向一个已存在的 Set 中添加项目可以使用 add() 方法或 addAll() 方法:
1. var elements = <String>{};
2. elements.add('fluorine');
3. elements.addAll(halogens);
使用 .length 可以获取 Set 中元素的数量:
1. var elements = <String>{};
2. elements.add('fluorine');
3. elements.addAll(halogens);
4. assert(elements.length == 5);
可以在 Set 字面量前添加 const 关键字创建一个 Set 编译时常量:
1. final constantSet = const {
2. 'fluorine',
3. 'chlorine',
4. 'bromine',
5. 'iodine',
6. 'astatine',
7. };
8. // constantSet.add('helium'); // 取消注释将导致出错 (Uncommenting this causes an error).
从 Dart 2.3 开始,Set 可以像 List 一样支持使用扩展操作符(… 和 …?)以及 Collection If 和 Collection For 操作。你可以查阅 List 扩展操作符和List 集合操作符获取更多相关信息。
Maps
通常来说, Map 是用来关联 keys 和 values 的对象。keys 和 values 可以是任何类型的对象。在一个 Map 对象中一个 key 只能出现一次。但是 value 可以出现多次。 Dart 中 Map 通过 Map 字面量和 Map 类型来实现。通常来说,Map 是一个键值对相关的对象。其中键和值都可以是任何类型的对象。每个 键 只能出现一次但是 值 可以重复出现多次。Dart 中 Map 提供了 Map 字面量以及 Map 类型两种形式的 Map。
下面是一对使用 Map 字面量创建 Map 的例子:
1. var gifts = {
2. // 键: 值
3. 'first': 'partridge',
4. 'second': 'turtledoves',
5. 'fifth': 'golden rings'
6. };
8. var nobleGases = {
9. 2: 'helium',
10. 10: 'neon',
11. 18: 'argon',
12. };
备忘:
Dart 将 gifts 变量的类型推断为 Map<String, String>,而降 nobleGases 的类型推断为 Map<int, String>。如果你向这两个 Map 对象中添加不正确的类型值,将导致运行时异常。你可以阅读 类型推断获取更多相关信息。
你也可以使用 Map 的构造器创建 Map:
1. var gifts = Map();
2. gifts['first'] = 'partridge';
3. gifts['second'] = 'turtledoves';
4. gifts['fifth'] = 'golden rings';
6. var nobleGases = Map();
7. nobleGases[2] = 'helium';
8. nobleGases[10] = 'neon';
9. nobleGases[18] = 'argon';
备忘:
这里为什么使用 Map() 而不是使用 new Map() 构造 Map 对象。因为从 Dart2 开始,构造对象的 new 关键字可以被省略掉。你可以查阅 构造函数的使用获取更多相关信息。
向现有的 Map 中添加键值对与 JavaScript 的操作类似:
1. var gifts = {'first': 'partridge'};
2. gifts['fourth'] = 'calling birds'; // 添加键值对 (Add a key-value pair)
从一个 Map 中获取一个值的操作也与 JavaScript 类似。
1. var gifts = {'first': 'partridge'};
2. assert(gifts['first'] == 'partridge');
如果检索的 Key 不存在于 Map 中则会返回一个 null:
1. var gifts = {'first': 'partridge'};
2. assert(gifts['fifth'] == null);
使用 .length 可以获取 Map 中键值对的数量:
1. var gifts = {'first': 'partridge'};
2. gifts['fourth'] = 'calling birds';
3. assert(gifts.length == 2);
在一个 Map 字面量前添加 const 关键字可以创建一个 Map 编译时常量:
1. final constantMap = const {
2. 2: 'helium',
3. 10: 'neon',
4. 18: 'argon',
5. };
7. // constantMap[2] = 'Helium'; // 取消注释将导致出错 (Uncommenting this causes an error).
从 Dart 2.3 Map 可以像 List 一样支持使用扩展操作符(… 和 …?)以及 Collection If 和 Collection For 操作。你可以查阅 List 扩展操作符和 List 集合操作符获取更多相关信息。
Runes 与 grapheme clusters
在 Dart 中,runes 公开了字符串的 Unicode 代码点。从 Dart 2.6 开始,使用 characters 包来访问或者操作用户感知的字符,也被称为 Unicode (扩展) grapheme clusters.。
Unicode 编码为每一个字母、数字和符号都定义了一个唯一的数值。因为 Dart 中的字符串是一个 UTF-16 的字符序列,所以如果想要表示 32 位的 Unicode 数值则需要一种特殊的语法。
通常使用 \uXXXX 来表示 Unicode 字符,XXXX 是一个四位数的 16 进制数字。例如心形字符(♥)的 Unicode 为 \u2665。对于不是四位数的 16 进制数字,需要使用大括号将其括起来。例如大笑的 emoji 表情(😆)的 Unicode 为 \u{1f600}。
如果你需要读写单个 Unicode 字符,可以使用 characters 包中定义的 characters getter。它将返回 Characters 作为一系列 grapheme clusters的字符串。下面是使用 characters API 的样例:
1. import 'package:characters/characters.dart';
2. ...
3. var hi = 'Hi 🇩🇰';
4. print(hi);
5. print('The end of the string: ${hi.substring(hi.length - 1)}');
6. print('The last character: ${hi.characters.last}\n');
输出取决于你的环境,看上去会像这样:
1. $ dart bin/main.dart
2. Hi 🇩🇰
3. The end of the string: ???
4. The last character: 🇩🇰
有关使用 characters 包操作字符串的详细信息,请参阅用于 characters 包的样例和 API 参考。
备忘:
在使用 List 操作 Rune 的时候需要小心,根据所操作的语种、字符集等不同可能会导致字符串出现问题,具体可参考 Stack Overflow 中的提问: 我如何在 Dart 中反转一个字符串?。
Symbols
Symbol 表示 Dart 中声明的操作符或者标识符,该类型的对象几乎不会被使用到,但是如果需要按名称引用它们的 API 时就非常有用。因为代码压缩后会改变这些符号的名称但不会改变具体的符号。
可以使用在标识符前加 # 前缀来获取 Symbol:
1. #radix
2. #bar
Symbol 字面量是编译时常量。
Functions
Dart 是一种真正面向对象的语言,所以即便函数也是对象并且类型为 Function,这意味着函数可以被赋值给变量或者作为其它函数的参数。你也可以像调用函数一样调用 Dart 类的实例。详情请查阅 可调用的类。
下面是定义一个函数的例子:
1. bool isNoble(int atomicNumber) {
2. return _nobleGases[atomicNumber] != null;
3. }
虽然高效 Dart 指南建议在公开的 API 上定义返回类型,不过即便不定义,该函数也依然有效:
1. isNoble(atomicNumber) {
2. return _nobleGases[atomicNumber] != null;
3. }
如果函数体内只包含一个表达式,你可以使用简写语法:
1. bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
语法 => 表达式 是 { return 表达式; } 的简写,=> 有时也称之为胖箭头语法。
备忘:
在 => 与 ; 之间的只能是 表达式 而非 语句。比如你不能将一个 if语句 放在其中,但是可以放置 条件表达式。
函数可以有两种形式的参数:必要参数 和 可选参数。必要参数定义在参数列表前面,可选参数则定义在必要参数后面。可选参数可以是 命名的 或 位置的。
备忘:
某些 API,特别是 Flutter 控件的构造器,它只使用命名参数,即便参数是强制性的。可以查阅下一节获取更多信息。
可选参数
可选参数分为命名参数和位置参数,可在参数列表中任选其一使用,但两者不能同时出现在参数列表中。
命名参数
当你调用函数时,可以使用 参数名: 参数值 的形式来指定命名参数。例如:
1. enableFlags(bold: true, hidden: false);
When defining a function, use{参数1, 参数2, …}to specify named parameters:
定义函数时,使用 {param1, param2, …} 来指定命名参数:
1. /// 设置 [bold] 和 [hidden] 标识……
2. /// Sets the [bold] and [hidden] flags...
3. void enableFlags({bool bold, bool hidden}) {...}
虽然命名参数是可选参数的一种类型,但是你仍然可以使用 @required 注解来标识一个命名参数是必须的参数,此时调用者则必须为该参数提供一个值。例如:
1. const Scrollbar({Key key, @required Widget child})
如果调用者想要通过 Scrollbar 的构造函数构造一个 Scrollbar 对象而不提供 child 参数,则会导致编译错误。
@required 注解定义在 meta 包中,可以直接导入 package:meta/meta.dart 包使用。
位置参数
使用 [] 将一系列参数包裹起来作为位置参数:
1. String say(String from, String msg, [String device]) {
2. var result = '$from says $msg';
3. if (device != null) {
4. result = '$result with a $device';
5. }
6. return result;
7. }
下面是不使用可选参数调用上述函数的示例:
1. assert(say('Bob', 'Howdy') == 'Bob says Howdy');
下面是使用可选参数调用上述函数的示例:
1. assert(say('Bob', 'Howdy', 'smoke signal') ==
2. 'Bob says Howdy with a smoke signal');
默认参数值
可以用 = 为函数的命名和位置参数定义默认值,默认值必须为编译时常量,没有指定默认值的情况下默认值为 null。
下面是设置可选参数默认值示例:
1. /// 设置 [bold] 和 [hidden] 标识……
2. /// Sets the [bold] and [hidden] flags ...
3. void enableFlags({bool bold = false, bool hidden = false}) {...}
5. // bold 的值将为 true;而 hidden 将为 false。
6. enableFlags(bold: true);
在老版本的 Dart 代码中会使用冒号(:)而不是 = 来设置命名参数的默认值。原因在于刚开始的时候命名参数只支持 :。不过现在这个支持已经过时,所以我们建议你现在都 使用 = 来指定默认值。
下一个示例将向你展示如何为位置参数设置默认值:
1. String say(String from, String msg,
2. [String device = 'carrier pigeon', String mood]) {
3. var result = '$from says $msg';
4. if (device != null) {
5. result = '$result with a $device';
6. }
7. if (mood != null) {
8. result = '$result (in a $mood mood)';
9. }
10. return result;
11. }
13. assert(say('Bob', 'Howdy') ==
14. 'Bob says Howdy with a carrier pigeon');
List 或 Map 同样也可以作为默认值。下面的示例定义了一个名为 doStuff() 的函数,并为其名为 list 和 gifts 的参数指定了一个 List 类型的值和 Map 类型的值。
1. void doStuff(
2. {List<int> list = const [1, 2, 3],
3. Map<String, String> gifts = const {
4. 'first': 'paper',
5. 'second': 'cotton',
6. 'third': 'leather'
7. }}) {
8. print('list: $list');
9. print('gifts: $gifts');
10. }
main() 函数
每个 Dart 程序都必须有一个 main() 顶级函数作为程序的入口,main() 函数返回值为 void 并且有一个 List<String> 类型的可选参数。
下面是一个 Web 应用的 main() 函数示例:
1. void main() {
2. querySelector('#sample_text_id')
3. ..text = 'Click me!'
4. ..onClick.listen(reverseText);
5. }
备忘:
上述代码中的 .. 语法称之为 级联调用。使用级联访问可以在一个对象上执行多个操作。
下面是使用命令行访问带参数的 main() 函数示例:
1. // 使用命令 dart args.dart 1 test 运行该应用
2. // Run the app like this: dart args.dart 1 test
3. void main(List<String> arguments) {
4. print(arguments);
6. assert(arguments.length == 2);
7. assert(int.parse(arguments[0]) == 1);
8. assert(arguments[1] == 'test');
9. }
你可以通过使用 参数库 来定义和解析命令行参数。
函数作为一级对象
可以将函数作为参数传递给另一个函数。例如:
1. void printElement(int element) {
2. print(element);
3. }
5. var list = [1, 2, 3];
7. // 将 printElement 函数作为参数传递。
8. list.forEach(printElement);
你也可以将函数赋值给一个变量,比如:
1. var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
2. assert(loudify('hello') == '!!! HELLO !!!');
该示例中使用了匿名函数。下一节会有更多与其相关的介绍。
匿名函数
大多数方法都是有名字的,比如 main() 或 printElement()。你可以创建一个没有名字的方法,称之为 匿名函数,或 Lambda表达式 或 Closure闭包。你可以将匿名方法赋值给一个变量然后使用它,比如将该变量添加到集合或从中删除。
匿名方法看起来与命名方法类似,在括号之间可以定义参数,参数之间用逗号分割。
后面大括号中的内容则为函数体:
([[类型] 参数[, …]]) { 函数体; };
下面代码定义了只有一个参数 item 且没有参数类型的匿名方法。List 中的每个元素都会调用这个函数,打印元素位置和值的字符串:
1. var list = ['apples', 'bananas', 'oranges'];
2. list.forEach((item) {
3. print('${list.indexOf(item)}: $item');
4. });
点击运行按钮执行代码。
如果函数体内只有一行语句,你可以使用胖箭头缩写法。粘贴下面代码到 DartPad 中并点击运行按钮,验证两个函数是否一致。
1. list.forEach( (item) => print('${list.indexOf(item)}: $item'));
词法作用域Dart 是词法有作用域语言,变量的作用域在写代码的时候就确定了,大括号内定义的变量只能在大括号内访问,与 Java 类似。下面是一个嵌套函数中变量在多个作用域中的示例:
1. 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 变量。
1. /// 返回一个将 [addBy] 添加到该函数参数的函数。/// Returns a function that adds [addBy] to the/// function's argument.Function makeAdder(num addBy) { return (num i) => addBy + i;}void main() { // 生成加 2 的函数。 var add2 = makeAdder(2); // 生成加 4 的函数。 var add4 = makeAdder(4); assert(add2(3) == 5); assert(add4(3) == 7);}
测试函数是否相等下面是顶级函数,静态方法和示例方法相等性的测试示例:
1. void foo() {} // 定义顶层函数 (A top-level function)class A { static void bar() {} // 定义静态方法 void baz() {} // 定义实例方法}void main() { var x; // 比较顶层函数是否相等。 x = foo; assert(foo == x); // 比较静态方法是否相等。 x = A.bar; assert(A.bar == x); // 比较实例方法是否相等。 var v = A(); // A 的实例 #1 var w = A(); // A 的实例 #2 var y = w; x = w.baz; // 这两个闭包引用了相同的实例对象,因此它们相等。 assert(y.baz == x); // 这两个闭包引用了不同的实例对象,因此它们不相等。 assert(v.baz != w.baz);}
返回值所有的函数都有返回值。没有显示返回语句的函数最后一行默认为执行 return null;。
1. foo() {}assert(foo() == null);
运算符Dart defines the operators shown in the following table.You can override many of these operators, as described inOverridable operators.
| 描述 | 运算符 |
|---|---|
| 一元后缀 | 表达式++ 表达式— () [] . ?. |
| 一元前缀 | -表达式 !表达式 ~表达式 ++表达式 —表达式 |
| 乘除法 | / % ~/ |
| 加减法 | + - |
| 位运算 | << >> >>> |
| 二进制与 | & |
| 二进制异或 | ^ |
| 二进制或 | | |
| 关系和类型测试 | >= > <= < as is is! |
| 相等判断 | == != |
| 逻辑与 | && |
| 逻辑或 | || |
| 空判断 | ?? |
| 条件表达式 | 表达式 1 ? 表达式 2 : 表达式 3 |
| 级联 | .. |
| 赋值 | = = /= += -= &= ^= 等等…… |
请注意:上述运算符优先级是对 Dart 解析器行为的效仿。更准确的描述,请参阅 Dart 语言规范 中的语法。一旦你使用了运算符,就创建了表达式。下面是一些运算符表达式的示例:
1. a++a + ba = ba == bc ? a : ba is T
在运算符表 中,运算符的优先级按先后排列,即第一行优先级最高,最后一行优先级最低,而同一行中,最左边的优先级最高,最右边的优先级最低。例如:% 运算符优先级高于 == ,而 == 高于 &&。根据优先级规则,那么意味着以下两行代码执行的效果相同:
1. // 括号提高了可读性。// Parentheses improve readability.if ((n % i == 0) && (d % i == 0)) …// 难以理解,但是与上面的代码效果一样。if (n % i == 0 && d % i == 0) …
请注意:对于有两个操作数的运算符,左边的操作数决定了运算符的功能。比如如果有一个 Vector 对象和一个 Point 对象,表达式 aVector + aPoint 中所使用的是 Vector 对象中定义的 + 运算符。### 算术运算符Dart 支持常用的算术运算符:
| 运算符 | 描述 |
|---|---|
+ |
加 |
– |
减 |
-表达式 |
一元负, 也可以作为反转(反转表达式的符号) |
| 乘 | |
/ |
除 |
~/ |
除并取整 |
% |
取模 |
示例:
1. assert(2 + 3 == 5);assert(2 - 3 == -1);assert(2 3 == 6);assert(5 / 2 == 2.5); // 结果是一个浮点数assert(5 ~/ 2 == 2); // 结果是一个整数assert(5 % 2 == 1); // 取余assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');
Dart 还支持自增自减操作。
Operator++var |
var = var + 1 (表达式的值为 var + 1) |
|---|---|
var++ |
var = var + 1 (表达式的值为 var) |
—var |
var = var – 1 (表达式的值为 var – 1) |
var— |
var = var – 1 (表达式的值为 var) |
示例:
1. var a, b;a = 0;b = ++a; // 在 b 赋值前将 a 增加 1。assert(a == b); // 1 == 1a = 0;b = a++; // 在 b 赋值后将 a 增加 1。assert(a != b); // 1 != 0a = 0;b = —a; // 在 b 赋值前将 a 减少 1。assert(a == b); // -1 == -1a = 0;b = a—; // 在 b 赋值后将 a 减少 1。assert(a != b); // -1 != 0
关系运算符下表列出了关系运算符及含义:
Operator== |
相等 |
|---|---|
!= |
不等 |
> |
大于 |
< |
小于 |
>= |
大于等于 |
<= |
小于等于 |
要判断两个对象 x 和 y 是否表示相同的事物使用 == 即可。(在极少数情况下,可能需要使用 identical() 函数来确定两个对象是否完全相同。)。下面是 == 运算符的一些规则:-假设有变量 x 和 y,且 x 和 y 至少有一个为 null,则当且仅当 x 和 y 均为 null 时 x == y 才会返回 true,否则只有一个为 null 则返回 false。-x.==(y) 将会返回值,这里不管有没有 y,即 y 是可选的。也就是说 == 其实是 x 中的一个方法,并且可以被重写。详情请查阅重写运算符。下面的代码给出了每一种关系运算符的示例:
1. assert(2 == 2);assert(2 != 3);assert(3 > 2);assert(2 < 3);assert(3 >= 3);assert(2 <= 3);
类型判断运算符as、is、is! 运算符是在运行时判断对象类型的运算符。
Operatoras |
类型转换(也用作指定类前缀)) |
|---|---|
is |
如果对象是指定类型则返回 true |
is! |
如果对象是指定类型则返回 false |
当且仅当 obj 实现了 T 的接口,obj is T 才是 true。例如 obj is Object 总为 true,因为所有类都是 Object 的子类。使用 as 操作符可以把对象转换为特定的类型。一般情况下可以将其当做 is 判定类型后调用所判定对象的函数的缩写形式。假设有如下代码:
1. if (emp is Person) { // 类型检查 emp.firstName = 'Bob';}
你可以使用 as 运算符进行缩写:
1. (emp as Person).firstName = 'Bob';
备忘:上述两种方式是有区别的:如果 emp 为 null 或者不为 Person 类型,则第一种方式最多只是不执行条件内代码,但是第二种方式则会抛出异常。### 赋值运算符可以使用 = 来赋值,同时也可以使用 ??= 来为值为 null 的变量赋值。
1. // 将 value 赋值给 a (Assign value to a)a = value;// 当且仅当 b 为 null 时才赋值b ??= value;
像 += 这样的赋值运算符将算数运算符和赋值运算符组合在了一起。
= |
–= |
/= |
%= |
>>= |
^= |
+= |
= |
~/= |
<<= |
&= |
|= |
下表解释了符合运算符的原理:
| 场景 | 复合运算 | 等效表达式 |
|---|---|---|
| 假设有运算符 op: | a op= b |
a = a op b |
| 示例: | a += b |
a = a + b |
下面的例子展示了如何使用赋值以及复合赋值运算符:
1. var a = 2; // 使用 = 赋值 (Assign using =)a = 3; // 赋值并做乘法运算 Assign and multiply: a = a 3assert(a == 6);
*### 逻辑运算符使用逻辑运算符你可以反转或组合布尔表达式。
| 运算符 | 描述 |
|---|---|
!!表达式 |
对表达式结果取反(即将 true 变为 false,false 变为 true) |
|| |
逻辑或 |
&& |
逻辑与 |
下面是使用逻辑表达式的示例:
1. if (!done && (col == 0 || col == 3)) { // …Do something…}
按位和移位运算符在 Dart 中,二进制位运算符可以操作二进制的某一位,但仅适用于整数。
| 运算符 | 描述 |
|---|---|
& |
按位与 |
| |
按位或 |
^ |
按位异或 |
~表达式 |
按位取反(即将 “0” 变为 “1”,“1” 变为 “0”) |
<< |
位左移 |
>> |
位右移 |
下面是使用按位和移位运算符的示例:
1. final value = 0x22;final bitmask = 0x0f;assert((value & bitmask) == 0x02); // 按位与 (AND)assert((value & ~bitmask) == 0x20); // 取反后按位与 (AND NOT)assert((value | bitmask) == 0x2f); // 按位或 (OR)assert((value ^ bitmask) == 0x2d); // 按位异或 (XOR)assert((value << 4) == 0x220); // 位左移 (Shift left)assert((value >> 4) == 0x02); // 位右移 (Shift right)
条件表达式Dart 有两个特殊的运算符可以用来替代 if-else 语句:- condition ? expr1 : expr2- If condition is true, evaluates expr1 (and returns its value);otherwise, evaluates and returns the value of expr2.条件 ? 表达式 1 : 表达式 2:如果条件为 true,执行表达式 1并返回执行结果,否则执行表达式 2 并返回执行结果。- expr1 ?? expr2- If expr1 is non-null, returns its value;otherwise, evaluates and returns the value of expr2.表达式 1 ?? 表达式 2:如果表达式 1 为非 null 则返回其值,否则执行表达式 2 并返回其值。如果赋值是根据布尔表达式则考虑使用 ?:。
1. var visibility = isPublic ? 'public' : 'private';
如果赋值是根据判定是否为 null 则考虑使用 ??。
1. String playerName(String name) => name ?? 'Guest';
上述示例还可以写成至少下面两种不同的形式,只是不够简洁:
1. // 相对使用 ?: 运算符来说稍微长了点。(Slightly longer version uses ?: operator).String playerName(String name) => name != null ? name : 'Guest';// 如果使用 if-else 则更长。String playerName(String name) { if (name != null) { return name; } else { return 'Guest'; }}
级联运算符(..)级联运算符(..)可以让你在同一个对象上连续调用多个对象的变量或方法。比如下面的代码:
1. querySelector('#confirm') // 获取对象 (Get an object). ..text = 'Confirm' // 使用对象的成员 (Use its members). ..classes.add('important') ..onClick.listen((e) => window.alert('Confirmed!'));
第一个方法 querySelector 返回了一个 Selector 对象,后面的级联操作符都是调用这个 Selector 对象的成员并忽略每个操作的返回值。上面的代码相当于:
1. var button = querySelector('#confirm');button.text = 'Confirm';button.classes.add('important');button.onClick.listen((e) => window.alert('Confirmed!'));
级联运算符可以嵌套,例如:
1. final addressBook = (AddressBookBuilder() ..name = 'jenny' ..email = 'jenny@example.com' ..phone = (PhoneNumberBuilder() ..number = '415-555-0100' ..label = 'home') .build()) .build();
在返回对象的函数中谨慎使用级联操作符。例如,下面的代码是错误的:
1. var sb = StringBuffer();sb.write('foo') ..write('bar'); // 出错:void 对象中没有方法 write (Error: method 'write' isn't defined for 'void').
上述代码中的 sb.write() 方法返回的是 void,返回值为 void 的方法则不能使用级联运算符。备忘:严格来说 .. 级联操作并非一个运算符而是 Dart 的特殊语法。### 其他运算符大多数其它的运算符,已经在其它的示例中使用过:
| 运算符 | 名字 | 描述 |
|---|---|---|
() |
使用方法 | 代表调用一个方法 |
[] |
访问 List | 访问 List 中特定位置的元素 |
. |
访问成员 | 成员访问符 |
?. |
条件访问成员 | 与上述成员访问符类似,但是左边的操作对象不能为 null,例如 foo?.bar,如果 foo 为 null 则返回 null ,否则返回 bar |
更多关于 ., ?. 和 .. 运算符介绍,请参考类.## 流程控制语句你可以使用下面的语句来控制 Dart 代码的执行流程:-if 和 else-for 循环-while 和 do-while 循环-break 和 continue-switch 和 case-assert使用 try-catch 和 throw 也能影响控制流,详情参考异常部分。### If 和 ElseDart 支持 if - else 语句,其中 else 是可选的,比如下面的例子。你也可以参考条件表达式。
1. if (isRaining()) { you.bringRainCoat();} else if (isSnowing()) { you.wearJacket();} else { car.putTopDown();}
与 JavaScript 不同的是,Dart 的 if 语句中的条件必须是一个布尔值,不能是其它类型。详情请查阅布尔值。### For 循环你可以使用标准的 for 循环进行迭代。例如:
1. var message = StringBuffer('Dart is fun');for (var i = 0; i < 5; i++) { message.write('!');}
在 Dart 语言中,for 循环中的闭包会自动捕获循环的 索引值 以避免 JavaScript 中一些常见的陷阱。假设有如下代码:
1. var callbacks = [];for (var i = 0; i < 2; i++) { callbacks.add(() => print(i));}callbacks.forEach((c) => c());
上述代码执行后会输出 0 和 1,但是如果在 JavaScript 中执行同样的代码则会输出两个 2。如果要遍历的对象实现了 Iterable 接口,则可以使用 forEach() 方法,如果不需要使用到索引,则使用 forEach 方法是一个非常好的选择:
1. candidates.forEach((candidate) => candidate.interview());
像 List 和 Set 等实现了 Iterable 接口的类还支持 for-in 形式的 迭代:
1. var collection = [0, 1, 2];for (var x in collection) { print(x); // 0 1 2}
While 和 Do-Whilewhile 循环会在执行循环体前先判断条件:
1. while (!isDone()) { doSomething();}
do-while 循环则会先执行一遍循环体 再 判断条件:
1. do { printLine();} while (!atEndOfPage());
Break 和 Continue使用 break 可以中断循环:
1. while (true) { if (shutDownRequested()) break; processIncomingRequests();}
使用 continue 可以跳过本次循环直接进入下一次循环:
1. for (int i = 0; i < candidates.length; i++) { var candidate = candidates[i]; if (candidate.yearsExperience < 5) { continue; } candidate.interview();}
上述代码中的 candidates 如果像 List 或 Set 一样实现了 Iterable 接口则可以简单地使用下述写法:
1. candidates .where((c) => c.yearsExperience >= 5) .forEach((c) => c.interview());
Switch 和 CaseSwitch 语句在 Dart 中使用 == 来比较整数、字符串或编译时常量,比较的两个对象必须是同一个类型且不能是子类并且没有重写 == 操作符。枚举类型非常适合在 Switch 语句中使用。备忘:Dart 中的 Switch 语句仅适用于有限的情况,比如使用解释器和扫描器的场景。每一个非空的 case 子句都必须有一个 break 语句,也可以通过 continue、throw 或者 return 来结束非空 case 语句。当没有 case 语句匹配时,可以使用 default 子句来匹配这种情况:
1. var command = 'OPEN';switch (command) { case 'CLOSED': executeClosed(); break; case 'PENDING': executePending(); break; case 'APPROVED': executeApproved(); break; case 'DENIED': executeDenied(); break; case 'OPEN': executeOpen(); break; default: executeUnknown();}
下面的例子忽略了 case 子句的 break 语句,因此会产生错误:
1. var command = 'OPEN';switch (command) { case 'OPEN': executeOpen(); // 错误: 没有 break case 'CLOSED': executeClosed(); break;}
但是,Dart 支持空的 case 语句,允许其以 fall-through 的形式执行。
1. var command = 'CLOSED';switch (command) { case 'CLOSED': // case 语句为空时的 fall-through 形式。 case 'NOWCLOSED': // case 条件值为 CLOSED 和 NOW_CLOSED 时均会执行该语句。 executeNowClosed(); break;}
*在非空 case 语句中想要实现 fall-through 的形式,可以使用 continue 语句配合 lable 的方式实现:
1. var command = 'CLOSED';switch (command) { case 'CLOSED': executeClosed(); continue nowClosed; // 继续执行标签为 nowClosed 的 case 子句。 nowClosed: case 'NOW_CLOSED': // case 条件值为 CLOSED 和 NOW_CLOSED 时均会执行该语句。 executeNowClosed(); break;}
每个 case 子句都可以有局部变量且仅在该 case 语句内可见。### 断言在开发过程中,可以在条件表达式为 false 时使用 - assert(条件, 可选信息); - 语句来打断代码的执行。你可以在本文中找到大量使用 assert 的例子。下面是相关示例:
1. // 确保变量值不为 null (Make sure the variable has a non-null value)assert(text != null);// 确保变量值小于 100。assert(number < 100);// 确保这是一个 https 地址。assert(urlString.startsWith('https'));
assert 的第二个参数可以为其添加一个字符串消息。
1. assert(urlString.startsWith('https'), 'URL ($urlString) should start with "https".');
assert 的第一个参数可以是值为布尔值的任何表达式。如果表达式的值为 true,则断言成功,继续执行。如果表达式的值为 false,则断言失败,抛出一个 AssertionError 异常。如何判断 assert 是否生效?assert 是否生效依赖开发工具和使用的框架:-Flutter 在调试模式时生效。-一些开发工具比如 dartdevc 通常情况下是默认生效的。-其他一些工具,比如 dart 以及 dart2js 通过在运行 Dart 程序时添加命令行参数 —enable-asserts 使 assert 生效。在生产环境代码中,断言会被忽略,与此同时传入 assert 的参数不被判断。## 异常Dart 代码可以抛出和捕获异常。异常表示一些未知的错误情况,如果异常没有捕获则会被抛出从而导致抛出异常的代码终止执行。与 Java 不同的是,Dart 的所有异常都是非必检异常,方法不一定会声明其所抛出的异常并且你也不会被要求捕获任何异常。Dart 提供了 Exception 和 Error 两种类型的异常以及它们一系列的子类,你也可以定义自己的异常类型。但是在 Dart 中可以将任何非 null 对象作为异常抛出而不局限于 Exception 或 Error 类型。### 抛出异常下面是关于抛出或者* 引发_ 异常的示例:
1. throw FormatException('Expected at least 1 section');
你也可以抛出任意的对象:
1. throw 'Out of llamas!';
备忘:优秀的代码通常会抛出 Error 或 Exception 类型的异常。因为抛出异常是一个表达式,所以可以在 => 语句中使用,也可以在其他使用表达式的地方抛出异常:
1. void distanceTo(Point other) => throw UnimplementedError();
捕获异常捕获异常可以避免异常继续传递(重新抛出异常除外)。捕获一个异常可以给你处理它的机会:
1. try { breedMoreLlamas();} on OutOfLlamasException { buyMoreLlamas();}
对于可以抛出多种异常类型的代码,也可以指定多个 catch 语句,每个语句分别对应一个异常类型,如果 catch 语句没有指定异常类型则表示可以捕获任意异常类型:
1. try { breedMoreLlamas();} on OutOfLlamasException { // 指定异常 buyMoreLlamas();} on Exception catch (e) { // 其它类型的异常 print('Unknown exception: $e');} catch (e) { // // 不指定类型,处理其它全部 print('Something really unknown: $e');}
如上述代码所示可以使用 on 或 catch 来捕获异常,使用 on 来指定异常类型,使用 catch 来捕获异常对象,两者可同时使用。你可以为 catch 方法指定两个参数,第一个参数为抛出的异常对象,第二个参数为栈信息 StackTrace 对象:<!—?code-excerpt “misc/lib/language_tour/exceptions.dart (try-catch-2)” replace=”/(e.*?)/[!$&!]/g”?—>
1. try { // ···} on Exception catch (e) { print('Exception details:\n $e');} catch (e, s) { print('Exception details:\n $e'); print('Stack trace:\n $s');}
关键字 rethrow 可以将捕获的异常再次抛出:
1. void misbehave() { try { dynamic foo = true; print(foo++); // 运行时错误 } catch (e) { print('misbehave() partially handled ${e.runtimeType}.'); rethrow; // 允许调用者查看异常。 }}void main() { try { misbehave(); } catch (e) { print('main() finished handling ${e.runtimeType}.'); }}
Finally可以使用 finally 语句来包裹确保不管有没有异常都执行代码,如果没有指定 catch 语句来捕获异常,则在执行完 finally 语句后再抛出异常:
``` 1. try { breedMoreLlamas();} finally { // 总是清理,即便抛出了异常。
(本文超出站点单文档体积上限,已截断)
