Dart语言指南(一)

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

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

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

不管什么时候须要有关语言功能的详细信息,请咨询Dart语言规范git

一个基本的Dart程序

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

// 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.web

这个程序使用的方法适用于全部(或几乎全部)Dart应用程序express

// This is a comment.编程

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

numapi

一种类型,其余内置类型,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没有关键字publicprotectedprivate。若是一个标识符如下划线(_)开头,则它的库是私有的。有关详细信息,请参阅库和可见性
  • 标识符能够以字母_开头,后面是字符数字的任意组合
  • 有时候区分表达式声明是很重要的,必须搞明白二者的含义。
  • 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 部分.

相关文章
相关标签/搜索