Dart语言指南(一) 顶

此文着重展示如何使用Dart语言的每一个主要功能,从变量和操作符到类和库,假设您已经知道如何用另一种编程语言。

学习更多Dart核心库,查看Dart库指南.

Note: 你可以使用DartPad运行大部分功能 .

无论何时需要有关语言功能的详细信息,请咨询Dart语言规范

一个基本的Dart程序

以下代码使用了许多Dart最基本的功能:

// Define a function.
printNumber(num aNumber) {
  print('The number is $aNumber.'); // Print to console.
}

// This is where the app starts executing.
main() {
  var number = 42; // Declare and initialize a variable.
  printNumber(number); // Call a function.
}

打开Dartpad.

这个程序使用的方法适用于所有(或几乎所有)Dart应用程序

// This is a comment.

使用//来进行注释,也可以使用/* */. 更多查看注释.

num

一种类型,其他内置类型,String,int,bool.

42

字面量即常量。

print()

控制台输出使用的函数。

'...' (或 "...")

表示字符串。

$variableName (或${expression})

字符串插值:包含字符串文字中的变量或表达式的字符串等价物. 更多信息查看字符串。

main()

应用程序执行的开始函数(特殊,必需). 更多信息查看main()函数

var

一种声明变量而不指定其类型的方式.

我们的代码遵循Dart风格指南中的约定。 例如,我们使用双空格缩进。

重要概念

当你学习这门语言时,记住这些事实和概念:

  • 你可以放在变量中的所有东西都是一个对象,每个对象都是一个类的实例。偶数,函数和null都是对象。所有对象都从Object类继承。
  • 指定静态类型(例如上例中的num) (您可能也有兴趣有一个特殊的类型:dynamic。)在Dart 1.x中指定静态类型是可选的,但Dart正在转向成为完全类型的安全语言。
  • 强类型模式下,静态和运行时检查确保您的代码是安全的,帮助您捕获开发中的错误,而不是在运行时。强力模式在Dart 1.x 中是可选的,但在Dart 2.0中不是可选的。
  • Dart在运行它之前解析所有的代码。可以向Dart提供提示,例如,使用类型或编译时常量来捕获错误或帮助您的代码运行得更快。
  • Dart支持顶层函数(如main()),以及链接到类或对象(分别为静态方法和实例方法)的函数。你也可以在函数内部创建函数(嵌套或局部函数)。
  • 同样,Dart支持顶级变量,以及一个类或对象的变量(静态变量和实例变量)。实例变量被称为字段或属性。
  • 与Java不同,Dart没有关键字public,protected和private。如果一个标识符以下划线(_)开头,则它的库是私有的。有关详细信息,请参阅库和可见性
  • 标识符可以以字母_开头,后面是字符数字的任意组合
  • 有时候区分表达式声明是很重要的,必须搞明白两者的含义。
  • Dart工具可以报告两种类型的问题:警告和错误。 警告只是表明您的代码可能无法正常工作,但它们并不妨碍您的程序执行。 错误可以是编译时或运行时。 编译时错误会阻止代码执行; 运行时错误导致代码执行时引发异常
  • Dart 1.x有两种运行模式:生产(production )和检查(checked)。 我们建议您在检查模式下进行开发和调试,并部署到生产模式。 生产模式是Dart程序的默认运行模式,针对速度进行了优化。 生产模式会忽略断言和静态类型。 检查模式是一种开发人员友好模式,可帮助您在运行时捕获某些类型的错误。 例如,如果将一个非数字赋给一个声明为num的变量,那么检查模式会抛出一个异常。

Dart 2.0 注意:在Dart 2.0中除去了检查模式。 有关更多信息,请参阅Dart 2.0更新日志

关键字

下表列出了Dart语言特别处理的词语。

上标1内置标识符,如果使用内置标识符作为类或类型名,将发生编译错误.

上标2是新的, 有限的保留字相关的,Dart的1.0版本后添加异步支持. 在任何标有async, async*,  sync*. 的功能体不能使用async, await, 或yield 作为标识符,更多信息查看异步支持

关键词表中的都是 保留字. 不能使用这些保留字作为标识符.

变量

以下是创建变量并为其分配值的示例:

var name = 'Bob';

变量是引用,名为name的变量包含一个值为“Bob”的String对象的引用.

默认值

未初始化的变量的初始值为null. 即使数值类型的变量最初也为空,因为数字是对象.

int lineCount;
assert(lineCount == null);
// Variables (even if they will be numbers) are initially null.

Note: 在生产模式下assert()调用被忽略. 在检查模式下,assert(condition)抛出异常,除非condition 为 true. 更多查看 Assert 部分.

可选类型

您可以选择在变量声明中添加静态类型:

String name = 'Bob';

添加类型是清楚表达您的意图的一种方式。 诸如编译器和编辑器之类的工具可以通过提供代码完成和对错误和代码完成的早期警告来使用这些类型来帮助您.

Note: 此页面遵循 style guide recommendation建议, 对于局部变量使用var, 而不是类型注释.

Final 和 const

如果变量的值不发生变化, 那么就可以使用final or const, 而不是var 或其它修饰符. final 修饰的变量只能设置一次值; const修饰的变量应当在声明处赋值,它是一个编译时的常数. (const变量是一种隐式的final变量.) 全局final变量或类变量在第一次使用时初始化.

Note: 实例变量可以是 final 而不能是const.

以下是创建和设置final变量的示例:

final name = 'Bob'; // Or: final String name = 'Bob';
// name = 'Alice';  // Uncommenting this causes an error

使用const修饰的变量作为编译时的常量。如果const变量处于类级别使用static const修饰. 在声明处为const修饰的变量赋值:

const bar = 1000000;       // Unit of pressure (dynes/cm2)
const atm = 1.01325 * bar; // Standard atmosphere

const 关键字不仅仅是声明常量变量. 您还可以使用它来create常量值,以及声明创建常量值的构造函数,任何变量都可以有一个常量值.

// Note: [] 创建一个空列表.
// const [] creates an empty, immutable list (EIA).
var foo = const [];   // foo is currently an EIA.
final bar = const []; // bar will always be an EIA.
const baz = const []; // baz is a compile-time constant EIA.

// You can change the value of a non-final, non-const variable,
// even if it used to have a const value.
foo = [];

// You can't change the value of a final or const variable.
// bar = []; // Unhandled exception.
// baz = []; // Unhandled exception.

更多关于使用 const 创建常量值, 查看 ListsMaps, and Classes.

内置类型

Dart语言对以下类型有特殊的支持:

  • numbers
  • strings
  • booleans
  • lists (arrays)
  • maps
  • runes (for expressing Unicode characters in a string)
  • symbols

您可以使用文字初始化任何这些特殊类型的对象. 例如, 'this is a string' 是一个字符串文字, true 是一个布尔文字.

因为Dart中的每个变量都指向一个对象 - class的一个实例—通常可以使用构造函数来初始化变量. 一些内置类型有自己的构造函数. 例如,您可以使用Map() 构造函数创建map对象, 使用代码 new Map().

数字

Dart中定义了两种数字类型:

int

整数值应在 -2 53 to 253之间

double

64位(双精度)浮点数, 由IEEE 754标准规定

int 和double 都是 num的子类. num类型包括基本的运算符,例如+, -, /, and *, 也可以在其他方法中找到 abs(),ceil(), 和 floor(),. (等位运算符,如 >>, 在 int 类中定义.) 如果num和它的子类型没有你寻找的, dart:math 库可能会有.

警告: 对于在-2 53 到 253 之外的数在javascript与Dart VM环境中运行处理的机制有所不同,因为Dart具有任意精度。 查看 issue 1533 获取更多

Integers 是没有小数点的数字。下面是一些定义整数文字的例子:

var x = 1;
var hex = 0xDEADBEEF;
var bigInt = 34653465834652437659238476592374958739845729;

如果一个数字包含一个小数,那么它是一个double。以下是定义双字面值的一些示例:

var y = 1.1;
var exponents = 1.42e5;

以下是将字符串转换成数字的方法,反之亦然:

// String -> int
var one = int.parse('1');
assert(one == 1);

// String -> double
var onePointOne = double.parse('1.1');
assert(onePointOne == 1.1);

// int -> String
String oneAsString = 1.toString();
assert(oneAsString == '1');

// double -> String
String piAsString = 3.14159.toStringAsFixed(2);
assert(piAsString == '3.14');

类型指定传统的按位移(<<, >>), AND (&), 和OR (|) 运算符。例如:

assert((3 << 1) == 6);  // 0011 << 1 == 0110
assert((3 >> 1) == 1);  // 0011 >> 1 == 0001
assert((3 | 4)  == 7);  // 0011 | 0100 == 0111

文字数字是编译时常数。许多算术表达式也是编译时常量,只要它们的操作数是编译时的常数来计算数字.

const msPerSecond = 1000;
const secondsUntilRetry = 5;
const msUntilRetry = secondsUntilRetry * msPerSecond;

Strings

Dart字符串采用UTF-16编码。 您可以使用单引号或双引号来创建字符串:

var s1 = 'Single quotes work well for string literals.';
var s2 = "Double quotes work just as well.";
var s3 = 'It\'s easy to escape the string delimiter.';
var s4 = "It's even easier to use the other delimiter.";

您可以使用 ${expression}将表达式的值放在字符串中。 如果表达式是标识符,则可以直接使用$标识符。 要得到一个对象的字符串,可以使用Dart调用该对象的 toString() 方法.

var s = 'string interpolation';

assert('Dart has $s, which is very handy.' ==
       'Dart has string interpolation, ' +
       'which is very handy.');
assert('That deserves all caps. ' +
       '${s.toUpperCase()} is very handy!' ==
       'That deserves all caps. ' +
       'STRING INTERPOLATION is very handy!');

注意:== 操作符测试两个对象是否相等。 如果两个字符串包含相同的代码单元序列,则它们是相同的.

您可以使用相邻字符串文字或+ 运算符连接字符串运算符连接字符串:

var s1 = 'String ' 'concatenation'
         " works even over line breaks.";
assert(s1 == 'String concatenation works even over '
             'line breaks.');

var s2 = 'The + operator '
         + 'works, as well.';
assert(s2 == 'The + operator works, as well.');

另一种创建多行字符串的方式:使用单引号或双引号的三重引号:

var s1 = '''
You can create
multi-line strings like this one.
''';

var s2 = """This is also a
multi-line string.""";

使用r前缀修饰字符串可以将转义符作为普通字符串:

var s = r"In a raw string, even \n isn't special.";

查看 Runes 了解跟多关于解析字符串中转义字符的信息.

文字字符串是编译时常量,任何内插的表达式都是一个编译时常数,可以计算为null或 numeric, string, 或 boolean 值.

// 以下变量可以插入到一个const修饰的字符串中。因为他们在编译时是可计算的.
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';

// 以下变量不能被插入到一个const修饰的字符串中。因为他们在编译时是不可计算的.
var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = const [1, 2, 3];

const validConstString = '$aConstNum $aConstBool $aConstString';
// const invalidConstString = '$aNum $aBool $aString $aConstList';//出错

使用strings的更多信息, 查看 Strings and regular expressions.

Booleans

为了表示布尔值,Dart有一个名为 bool的类型. 只有两个对象是bool类型:布尔文字true和false,Only two objects have type bool: the boolean literals true and false, 它们都是编译时常量。 .

当Dart需要一个布尔值时,只有值 true 被视为true. 所有其他值被视为假。 与JavaScript不同,诸如 1"aString", 和 someObject 之类的值都被视为false.

例如,请思考以下代码,该代码既有JavaScript也可以是Dart代码:

var name = 'Bob';
if (name) {
  // Prints in JavaScript, not in Dart.
  print('You have a name!');
}

如果您将此代码作为JavaScript运行,则会打印“You have a name!”,因为 name 是一个非空对象. 在Dart中的production mode下, 由于name 转换为 false (因为name != true)所以上述代码根本不打印. 在Dart的checked mode模式下, 前面的代码抛出一个异常,因为name 变量不是一个bool .

这是另一个在JavaScript和Dart中行为不同的代码示例:

if (1) {
  print('JS prints this line.');
} else {
  print('Dart in production mode prints this line.');
  // However, in checked mode, if (1) throws an
  // exception because 1 is not boolean.
}

Note:前两个例子仅在生产模式下工作,而不是检查模式。 在检查模式下,如果在布尔值被期望时使用非布尔值,则抛出异常.

Dart中对Booleans的设计主要是为了避免许多可以被看作是true的值,对于我们来说,不能使用if (nonbooleanValue)来判断,而应该明确检查值:

// Check for an empty string.
var fullName = '';
assert(fullName.isEmpty);

// Check for zero.
var hitPoints = 0;
assert(hitPoints <= 0);

// Check for null.
var unicorn;
assert(unicorn == null);

// Check for NaN.
var iMeantToDoThis = 0 / 0;
assert(iMeantToDoThis.isNaN);

Lists

也许在几乎每个编程语言中最常见的集合是array或对象的有序组。在Dart中array是 List 对象,所以我们通常只是调用lists.

Dart列表文字看起来像JavaScript数组文字。 这是一个简单的Dart列表:

var list = [1, 2, 3];

Lists 使用基于零的索引,其中0是第一个元素的索引,list.length - 1 是最后一个元素的索引。 您可以像JavaScript一样获取列表的长度并引用列表元素:

var list = [1, 2, 3];
assert(list.length == 3);
assert(list[1] == 2);

list[1] = 1;
assert(list[1] == 1);

要创建一个编译时常量的列表,请在列表文字之前添加const 修饰:

var constantList = const [1, 2, 3];
// constantList[1] = 1; // Uncommenting this causes an error.

List 类型有许多方便操作列表的方法。 有关列表的更多信息,请参见 Generics 和 Collections.

Maps

map是一种将key和value相关联的对象,key和value都可以是任何对象,key 不可重复,value value 可重复。dart中支持map字面量和 Map类型来构建map.

这里有几个简单的Dart map,使用map字面量创建:

var gifts = {
// Keys      Values
  'first' : 'partridge',
  'second': 'turtledoves',
  'fifth' : 'golden rings'
};

var nobleGases = {
// Keys  Values
  2 :   'helium',
  10:   'neon',
  18:   'argon',
};

你也可以使用map构造器创建map对象:

var gifts = new Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';

var nobleGases = new Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';

向现有map对象添加新的键值对,就像在JavaScript中一样:

var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds'; // Add a key-value pair

从map对象中获取值与JavaScript中的值相同:

var gifts = {'first': 'partridge'};
assert(gifts['first'] == 'partridge');

如果调用的key不在map对象中,将返回null:

var gifts = {'first': 'partridge'};
assert(gifts['fifth'] == null);

使用 .length 方法取得map对象的长度:

var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds';
assert(gifts.length == 2);

要创建一个编译时常量的map对象,请在map文字之前添加const 修饰:

final constantMap = const {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};

// constantMap[2] = 'Helium'; // Uncommenting this causes an error.

For more information about maps, see Generics and Maps.

Runes

在Dart中,符号是字符串的UTF-32编码.

Unicode为所有世界写作系统中使用的每个字母,数字和符号定义唯一的数值,因为Dart字符串是UTF-16代码单元的序列,因此在字符串中表达32位Unicode值需要特殊语法.

表达Unicode代码点的通常方式是 \uXXXX, 其中XXXX是一个4位数的十六进制值. 例如,心脏字符 (♥) 是 \u2665. 要指定多于或少于4个十六进制数字,请将该值放在大括号中. 例如,笑声表情符号 (?) 是 \u{1f600}.

String 类有几个属性可用于提取符文信息. codeUnitAt 和 codeUnit 属性返回16位代码单元. 使用 runes 属性来获取字符串的符文.

以下示例说明了符文,16位代码单元和32位代码点之间的关系单击运行按钮( ) 以查看运行中的符文

main() {
  var clapping = '\u{1f44f}';
  print(clapping);
  print(clapping.codeUnits);
  print(clapping.runes.toList());

  Runes input = new Runes(
      '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
  print(new String.fromCharCodes(input));
}

Note: 使用列表操作操纵符文时要注意。 根据具体的语言,字符集和操作,这种方法很容易分解。 . 更多信息查看如何反转Dart中的字符串?在 Stack Overflow 上.

Symbols

Symbol对象表示在Dart程序中声明的操作符或标识。你可能不会需要使用这些符号,但他们对于由名字指向的API是很有用的,因为时常要改变的是标识符的名字,而不是标识符的符号.

为了得到符号的标识,使用符号的文本,只是 # 后跟标识符:

#radix
#bar

Symbol 是编译时常量.

关于symbols的更多信息, 查看 dart:mirrors - reflection.

Functions

Dart是一个真正的面向对象语言,所以即使函数也是对象,也有一个类型 Function. 这意味着可以将函数分配给变量或作为参数传递给其他函数. Y您也可以调用Dart类的实例,就像它是一个函数一样. 更多查看 Callable classes.

以下是实现函数的示例:

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

尽管Dart强烈建议使用为公共API键入注解中定义的类型注解,但是你也可以忽略它:

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

如果Funtion只有一个表达式,你可以使用简洁语法:

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

=> expr 是 { return expr; }的简写形式. => 符号有时又被叫做 胖箭头表达式.

Note: 仅仅一个 表达式—不是一个语句块—能够出现在箭头 (=>) 和分号 (;)之间.比如, 你不能使用 if 语句块, 但你可以使用 条件表达式 .

一个function可以拥有两种类型的参数: 必选参数 和 可选参数. 必选参数先列出来, 紧接着是可选参数.

可选参数(Optional parameters)

可选参数分为 命名参数 和 位置参数 ,一个函数中只能使用其中一中,即不能同时存在于一个函数中。

可选命名参数(Optional named parameters)

当调用一个函数时, 你可以使用 参数名的形式指定命名参数. 比如:

enableFlags(bold: true, hidden: false);

当定义函数时, 使用 {param1param2, …} 来指定命名参数:

/// Sets the [bold] and [hidden] flags to the values
/// you specify.
enableFlags({bool bold, bool hidden}) {
  // ...
}

可选位置参数(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');

参数默认值

函数可以使用=来为可选命名和可选位置参数制定默认值. 默认值必须是编译时常量. 如果没有提供默认值,默认值是 null.

下边为可选命名参数指定默认值:

/// Sets the [bold] and [hidden] flags to the values you
/// specify, defaulting to false.
void enableFlags({bool bold = false, bool hidden = false}) {
  // ...
}

// bold will be true; hidden will be false.
enableFlags(bold: true);

版本要记: 旧代码可能使用冒号 (:)代替 = 来设置命名参数的默认值. 原因是在 SDK 1.21版本以前, 只有: 被命名参数支持. 那种支持接近弃用, 我们推荐 使用 = 指定默认值, 并且 specify an SDK version of 1.21 or higher.

下面的例子是为位置参数指定默认值:

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 参数指定了默认数组, 为gifts 参数指定默认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() 函数返回  和一个 可选的List<String> 参数.

web应用中的main() 函数:

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

Note: .. 语句在代码前被叫做 级联. 使用级联, 你可以对单个对象执行多重操作.

命令行应用中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 library 定义和解析命令行参数.

Functions最为最好的对象

你可以为一个函数传递另一个函数:

printElement(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(). 你也可以创建一个无名函数叫做匿名函数,或是lambda表达式闭包. 你可以给一个变量指定匿名函数, 你可以将它添加到集合或移除.

匿名函数看起来与命名函数及其相似— 零个或更多参数, 被逗号和可选的类型修饰符分割, 在圆括号中间. 代码块包含在函数体内部:

([[Typeparam1[, …]]) {  codeBlock }; 

下面的匿名函数包含一个未指定类型的参数i. 函数调用list中的每一项, 打印包含值和索引的字符串.

var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums'];
list.forEach((i) {
  print(list.indexOf(i).toString() + ': ' + i);
});

点击运行按钮 (  )执行代码.

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

如果函数只包含一个语句, 可以使用胖箭头.将以下行粘贴到DartPad中,然后单击运行以验证其功能是否相同。

list.forEach((i) => print(list.indexOf(i).toString() + ': ' + i));

语法作用域(Lexical scope)

Dart是一种具备词法作用域的语言, 意味着变量的作用范围是固定不变的, 仅仅通过布局代码. 你能“向大括号之外追溯”来看一个变量的作用域.

下面是一个嵌套函数和变量在每一级作用域的例子:

var topLevel = true;

main() {
  var insideMain = true;

  myFunction() {
    var insideFunction = true;

    nestedFunction() {
      var insideNestedFunction = true;

      assert(topLevel);
      assert(insideMain);
      assert(insideFunction);
      assert(insideNestedFunction);
    }
  }
}

注意 为什么nestedFunction() 能使用每一级的变量, 始终能访问第一级的变量.

语法闭包(Lexical closures)

一个闭包是一个能够访问其词法作用域内变量的函数对象,甚至当这个函数在其原有作用域之外被调用时.

函数内部会包含在临近作用域内所定义的变量. 在下例中, makeAdder() 捕获变量 addBy. 无论函数返回到哪里,它都存储着addBy.

/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(num addBy) {
  return (num i) => addBy + i;
}

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

相等测试函数

测试顶级函数, 静态方法, 和 实例方法 :

foo() {}               // A top-level function

class A {
  static void bar() {} // A static method
  void baz() {}        // An instance method
}

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 = new A(); // Instance #1 of A
  var w = new 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; 依赖于函数体.

操作符(Operators)

Dart定义了下表这些操作符. 你可以复写这些操作符, 详情见可覆盖的操作符.

当使用操作符时, 创建表达式:

a++
a + b
a = b
a == b
a ? b: c
a is T

在 操作符表中, 在一行中的每个操作符都比它后边的优先级高. 比如, 乘性运算符 % 比等值运算符==优先, 比逻辑与&&优先. 优先级意味着下边两行代码执行结果相同:

// 1: () 提高可读性.
if ((n % i == 0) && (d % i == 0))

// 2: 难以阅读,但是等价.
if (n % i == 0 && d % i == 0)

警告: 操作符有两个操作数,最左操作数决定那个操作符被使用. 比如, 如果有一个Vector对象和一个Point对象, aVector + aPoint + 由Vector决定和使用.

算数操作符

Dart支持常用的算数操作符.

举例:

assert(2 + 3 == 5);
assert(2 - 3 == -1);
assert(2 * 3 == 6);
assert(5 / 2 == 2.5);   // 返回double类型
assert(5 ~/ 2 == 2);    // 返回integer类型
assert(5 % 2 == 1);     // 取余

print('5/2 = ${5~/2} r ${5%2}'); // 5/2 = 2 r 1

Dart也支持自加和自减.

举例:

var a, b;

a = 0;
b = ++a;        // b获得a的增量值.
assert(a == b); // 1 == 1

a = 0;
b = a++;        // 在b得到a的值后,a自增.
assert(a != b); // 1 != 0

a = 0;
b = --a;        // 在b得到a的值前,a自减.
assert(a == b); // -1 == -1

a = 0;
b = a--;        // 在b得到a的值后,a自增.
assert(a != b); // -1 != 0

等价和关系运算符

下表列出了等价和关系运算符的含义.

要测试两个对象x和y是否代表相同的东西,请使用 == 运算符。 (在极少数情况下,您需要知道两个对象是否完全相同的对象,请改用 identical() 函数。)== 操作符使用如下:

  1. 如果 xy 是 null: 如果两个都为null返回true, 如果只有其中一个为null返回false
  2. x.==(y)返回一个函数调用的结果. (这个调用是正确的,像 == 这样的运算符实际上是由第一个操作数所调用的一个方法。你可以重写大部分运算符,包括==, 你可以在覆盖操作符中看到.)

以下是使用每个等价和关系运算符的示例:

assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);

类型测试操作符

asis,和 is! 操作符可以在运行时检查类型.

如果obj 实现了T定义的接口,那么obj is T 返回结果为true,例如: obj is Object 始终返回true (Object是所有类的父类).

使用as 操作符可以把一个对象转换为特定类型。一般来说,如果在is 测试之后还有一些关于对象的表达式,你可以把as``当做是is测试的一种简写。思考下面这段代码:

if (emp is Person) { // 类型检查
  emp.firstName = 'Bob';
}

您可以使用as 操作符缩短代码:

(emp as Person).firstName = 'Bob';

Note: as 和 is 代码不等同. 如果emp 为null或不是Person, 第一段代码(使用 is)不做如何操作; 第二段代码(使用 as) 抛出一个异常.

赋值运算符

正如你所见,你可以使用 =操作符为变量分配值. 只有为值为空的变量分配值时, 可以使用??= 操作符.

a = value;   // 为a分配值
b ??= value; // 如果b为null,为b分配值;
             // 否则, b维持原值

复合赋值运算符将操作与赋值相结合.

复合赋值运算符工作原理:

以下示例使用赋值和复合赋值运算符:

var a = 2;           // 使用 = 赋值
a *= 3;              // 赋值和乘操作: a = a * 3
assert(a == 6);

逻辑运算符

你可以使用逻辑运算符与布尔表达式组合使用或取反.

下面是使用逻辑操作符的例子:

if (!done && (col == 0 || col == 3)) {
  // ...Do something...
}

位和位移运算符

在Dart中你可以操纵数字的各个位。 通常,您将对整数使用这些按位运算符和移位运算符的.

下面是使用位运算符和位移运算符的例子:

final value = 0x22;
final bitmask = 0x0f;

assert((value & bitmask)  == 0x02);  // 与
assert((value & ~bitmask) == 0x20);  // 与非
assert((value | bitmask)  == 0x2f);  // 或
assert((value ^ bitmask)  == 0x2d);  // 异或
assert((value << 4)       == 0x220); // 左位移
assert((value >> 4)       == 0x02);  // 右位移

条件运算符

Dart中提供了两个操作符的运算符,让您简洁地评估表达式,否则可能需要 if-else语句:

condition ? expr1 : expr2

如果condition 为true, 执行 expr1 (并且返回它的值); 否则执行expr2并返回它的值.

expr1 ?? expr2

如果expr1非空, 返回它的值; 否则执行expr2并返回它的值.

当你需要基于布尔表达式进行赋值操作时, 可以考虑使用?:.

var finalStatus = m.isFinal ? 'final' : 'not final';

如果布尔表达式为了测试是否为null,可以考虑使用??.

String toString() => msg ?? super.toString();

上一个例子还有另外两种实现方式,但是不够简洁:

// ?: 操作符使用稍长版本.
String toString() => msg == null ? super.toString() : msg;

//使用if-else 语句 代码非常长.
String toString() {
  if (msg == null) {
    return super.toString();
  } else {
    return msg;
  }
}

级联运算符 (..)

级联(..)允许您对同一对象进行一系列操作。 除了函数调用之外,还可以访问该对象上的字段。 这通常可以节省您创建临时变量的步骤,并允许您编写更流畅的代码。.

思考下列代码:

querySelector('#button') // 得到一个对象.
  ..text = 'Confirm'   // 使用它的成员.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));

第一个方法叫, querySelector(), 返回选择器对象. 级联符号之后的代码在此选择器对象上运行,忽略可能返回的任何后续值.

上一个例子等价于:

var button = querySelector('#button');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));

级联可以嵌套. 比如:

final addressBook = (new AddressBookBuilder()
      ..name = 'jenny'
      ..email = 'jenny@example.com'
      ..phone = (new PhoneNumberBuilder()
            ..number = '415-555-0100'
            ..label = 'home')
          .build())
    .build();

在返回实际对象的函数上构建级联关系要注意,例如, 下面的代码执行失败:

// 不能运行
var sb = new StringBuffer();
sb.write('foo')..write('bar');

方法sb.write() 调用返回void(空),不能在void上构建级联关系.

Note: 严格来说,级联的".."符号不是运算符。 它只是Dart语法的一部分。

其它运算符

在其他示例中,您已经看到大部分剩余的运算符:

查看更多关于 .?., 和 .. 操作符, 查看 Classes.

控制语句

你可以使用下列任何一种控制Dart代码流:

  • if 和 else
  • for 循环
  • while 和 do-while 循环
  • break 和 continue
  • switch 和 case
  • assert

你也可以使用 try-catch 和 throw语句来影响控制流, 详见 Exceptions.

If 和 else

Dart 支持 if 语句和可选的 else 语句,如下例所示. 也可以查看条件表达式.

if (isRaining()) {
  you.bringRainCoat();
} else if (isSnowing()) {
  you.wearJacket();
} else {
  car.putTopDown();
}

记住:不像Javascript,Dart将除true之外的所有值都视为 false. 查看 Booleans 了解更多信息.

For 循环

你可以使用标准 for 循环实现代码反复使用.如:

var message = new StringBuffer("Dart is fun");
for (var i = 0; i < 5; i++) {
  message.write('!');
}

Dart中闭包在for 循环里能捕获索引值, 避免了JavaScript中常见的陷阱. 比如, 思考:

var callbacks = [];
for (var i = 0; i < 2; i++) {
  callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());

如我们预期先输出0 然后输出1. 相反, 在JavaScript中先打印 2 然后打印2 .

如果你要在可迭代的对象上执行迭代, 你可以使用 forEach() 方法. 如果你不需要知道迭代计数器那么使用forEach() 是一个很好的选择:

candidates.forEach((candidate) => candidate.interview());

List和Set等可迭代类也支持for-in形式的 迭代:

var collection = [0, 1, 2];
for (var x in collection) {
  print(x);
}

While and do-while

while循环在循环之前判断表达式:

while (!isDone()) {
  doSomething();
}

do-while 循环在循环之后 判断表达式:

do {
  printLine();
} while (!atEndOfPage());

Break 和 continue

使用 break 停止循环:

while (true) {
  if (shutDownRequested()) break;
  processIncomingRequests();
}

使用 continue 跳过本次循环执行下一次循环迭代:

for (int i = 0; i < candidates.length; i++) {
  var candidate = candidates[i];
  if (candidate.yearsExperience < 5) {
    continue;
  }
  candidate.interview();
}

如果你使用 可迭代的对象你可能写出不一样的代码 像list或set:

candidates.where((c) => c.yearsExperience >= 5)
          .forEach((c) => c.interview());

Switch 和 case

Switch语句在Dart中使用==比较整型, 字符串, 或编译时常量. 比较的对象必须都是同一个类的实例 (而不是其任何子类型), 而且类没有复写==枚举类型 非常适用于switch 语句.

Note: Dart中的Switch语句是在选择有限的情况下使用, 比如翻译器或者扫描器.

规定:每一个非空 case 分句以 break 语句结束,其它被允许可作为非空 case 分句结束的还有continuethrow, 或 return 语句.

当与case 分句未形成匹配到时,使用default分句执行代码 :

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 语句 , 从而造成了错误:

var command = 'OPEN';
switch (command) {
  case 'OPEN':
    executeOpen();
    // ERROR: Missing break causes an exception!!

  case 'CLOSED':
    executeClosed();
    break;
}

然而, Dart支持 case 分句内容为空, 被允许的一种形式的贯穿:

var command = 'CLOSED';
switch (command) {
  case 'CLOSED': // Empty case falls through.
  case 'NOW_CLOSED':
    // Runs for both CLOSED and NOW_CLOSED.
    executeNowClosed();
    break;
}

如果你想实现贯穿, 可以使用 continue 语句和一个标记:

var command = 'CLOSED';
switch (command) {
  case 'CLOSED':
    executeClosed();
    continue nowClosed;
    // Continues executing at the nowClosed label.

nowClosed:
  case 'NOW_CLOSED':
    // Runs for both CLOSED and NOW_CLOSED.
    executeNowClosed();
    break;
}

case 分句可以拥有局部变量, 并且只可以在该分句的作用域里使用.

Assert

如果布尔条件为假,使用assert语句来中断正常执行,您可以在整个指南中找到assert语句的例子。 这里还有一些:

// Make sure the variable has a non-null value.
assert(text != null);

// Make sure the value is less than 100.
assert(number < 100);

// Make sure this is an https URL.
assert(urlString.startsWith('https'));

Note: Assert 语句只在检查模式下执行. 在生产模式下不起作用.

将消息附加到断言, 添加一个字符串作为第二个参数.

assert(urlString.startsWith('https'),
    'URL ($urlString) should start with "https".');

版本要点: 第二个参数在SDK 1.22开始引入.

assert的第一个参数可以是解析为布尔值或函数的任何表达式。 如果表达式的值或函数的返回值为真,则断言成功并继续执行。 如果它为false,则断言失败,并抛出异常 AssertionError) .

异常Exceptions

Dart代码可以抛出和捕获异常. 异常表示发生了某些意外的错误. 如果异常未被捕获, 引起异常的巢室将被挂起,并且巢室有 和其程序被销毁。.

与Java不同, Dart中的所有异常都属于未检查异常.方法也不声明抛出什么异常,你也没有必要捕获异常.

Dart提供 Exception 和 Error 类型,以及许多预定义的子类型. 你也可以定义自己的异常. 但是,Dart程序可以抛出任何非空对象 - 而不仅仅是Exception和Error对象 - 作为异常。.

Throw

这是一个抛出或 唤起异常的例子:

throw new FormatException('Expected at least 1 section');

你也可以随便抛出一个对象:

throw 'Out of llamas!';

因为抛出异常是一个表达式,你可以在=> 语句中以及允许表达式的任何地方抛出异常:

distanceTo(Point other) =>
    throw new UnimplementedError();

Catch

捕获异常会阻止异常传播(除非您重新抛出异常)。 捕获一个异常让你能够处理它:

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  buyMoreLlamas();
}

要处理可能引发多种类型异常的代码,可以指定多个catch子句。 匹配抛出的对象的类型的第一个catch子句处理异常。 如果catch子句未指定类型,则该子句可以处理任何类型的抛出对象:

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // A specific exception
  buyMoreLlamas();
} on Exception catch (e) {
  // Anything else that is an exception
  print('Unknown exception: $e');
} catch (e) {
  // No specified type, handles all
  print('Something really unknown: $e');
}

如上述代码所示,您可以使用 on 或 catch 或两者。 当您需要指定异常类型时使用 on 。 当异常处理程序需要异常对象时使用 catch.

你可以为catch()指定两个参数. 第一个是抛出的异常, 第二个是 栈轨迹 ( StackTrace 对象).

  ...
} on Exception catch (e) {
  print('Exception details:\n $e');
} catch (e, s) {
  print('Exception details:\n $e');
  print('Stack trace:\n $s');
}

如果处理异常的一部分,同时允许它传播,请使用rethrow 关键字.

final foo = '';

void misbehave() {
  try {
    foo = "You can't change a final variable's value.";
  } catch (e) {
    print('misbehave() partially handled ${e.runtimeType}.');
    rethrow; // Allow callers to see the exception.
  }
}

void main() {
  try {
    misbehave();
  } catch (e) {
    print('main() finished handling ${e.runtimeType}.');
  }
}

Finally

为确保某些代码无论有无异常都执行,请使用finally 子句. 如果catch 子句没有相匹配的异常, 则在finally 子句执行后,异常继续传播:

try {
  breedMoreLlamas();
} finally {
  // Always clean up, even if an exception is thrown.
  cleanLlamaStalls();
}

finally 子句应该放在所有 catch 子句之后:

try {
  breedMoreLlamas();
} catch(e) {
  print('Error: $e');  // Handle the exception first.
} finally {
  cleanLlamaStalls();  // Then clean up.
}

学习更多查看 Exceptions 部分.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏cmazxiaoma的架构师之路

你应该会的一道多线程笔试题

32330
来自专栏偏前端工程师的驿站

Java魔法堂:深入正则表达式API

目录                               一、前言 二、正则表达式的使用诉求 三、java.util.regex包 四、java.lan...

21350
来自专栏SeanCheney的专栏

Python基础回顾基本数据类型和运算容器分支和循环函数、生成器和类map, reduce和filter列表生成(list comprehension)字符串文件操作和pickle异常多进程(mult

Python shell输入import this 可以看到The Zen of Python 基本数据类型和运算 基本数据类型 Python中最基本的数据类...

49170
来自专栏陈满iOS

iOS基础·属性的修饰词与setter的关系

可以自己手动为实例变量在头文件 中声明setter、getter方法,并在实现文件中实现setter、getter方法。你也可以不声明不实现,但不要再企图调用s...

19520
来自专栏偏前端工程师的驿站

(cljs/run-at (->JSVM :browser) "语言基础")

前言  两年多前知道cljs的存在时十分兴奋,但因为工作中根本用不上,国内也没有专门的职位于是搁置了对其的探索。而近一两年来又刮起了函数式编程的风潮,恰逢有幸主...

23070
来自专栏Hongten

python开发_python代码风格(coding style)

13710
来自专栏编程微刊

JS数组去重的6种算法实现以上就是为大家提供的6种JS数组去重的算法实现,希望对大家的学习有所帮助。

21120
来自专栏数据结构与算法

计算(calc.cpp)

计算(calc.cpp) 【问题描述】 小明在你的帮助下,破密了Ferrari设的密码门,正要往前走,突然又出现了一个密码门,门上有一个算式,其中只有“(”,“...

411100
来自专栏逆向与安全

从虚拟机角度看Java多态->(重写override)的实现原理

工具与环境: Windows 7 x64企业版 Cygwin x64 jdk1.8.0_162 openjdk-8u40-src-b25-10_feb_201...

23900
来自专栏Python绿色通道

Python入门三部曲(二)

如果不确定使用del语句还是pop()方法,有一个简单的标准:如果你要从列表中删除的一个元素,且不再以任何方式使用它,就使用del语句;如果你要在删除元素后还能...

12730

扫码关注云+社区

领取腾讯云代金券