Dart官方文档翻译(一)(Dart之旅)

这篇文章将会向你展示如何使用Dart的每一个知识点,包括变量,操作符,类,类库,但这是在你已经有一个其他语言的编码经验的前提下。
为了学习Dart更多关于Dart的核心类库,请查看A Tour of the Dart Libraries,当你想知道更多语言特征,挺查阅Dart language specification

你可以通过DartPad 来尝试运行Dart代码:Open DartPad

一个基本的Dart程序

// Define a function.
printInteger(int 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.
  printInteger(number); // Call a function.
}

以下是此程序使用的适用于所有(或几乎所有)Dart应用程序的内容:

// This is a comment.

单行注释。Dart还支持多行和文档注释。有关详情,请参阅注释

int

整数类型。一些其他的内置类型StringListbool

42

一个整数字面值,这是一个编译时常量。

print()

一个方便的输出函数。

'...' (or "...")

字符串字面值。

$variableName(或)${expression}

字符串插值:包括字符串字面值内部的变量或表达式的字符串。有关更多信息,请参阅 字符串

main()

一个特殊的顶级(top)的必须的(requared)app应用的入口函数,有关更多信息,请参阅 main()函数

var

一种声明变量而不指定其类型的方法。

重要的概念

当您了解Dart语言时,请记住以下事实和概念:

  • 变量所指向的所有值都是对象,每个对象都是一个的实例。数字,函数(函数也是一个变量)和 null对象等。所有对象都继承自Object类。
  • 尽管Dart是强类型的,但类型声明是可选的,因为Dart可以推断类型。在上面的代码中,number 推断为类型int。如果要明确说明不需要任何类型,请 使用特殊类型dynamic
  • Dart支持泛型类型,如List<int>(整数List)或List<dynamic>(任何类型的对象List)。
  • Dart支持顶级函数(例如main()),以及绑定到类或对象的函数(分别是静态和实例方法)。您还可以在函数内创建函数(嵌套函数或本地函数)。
  • 类似地,Dart支持顶级变量,以及绑定到类或对象的变量(静态和实例变量)。实例变量有时称为字段或属性。
  • 与Java,Dart不具备关键字publicprotectedprivate。如果标识符以下划线(_)开头,则它对其库是私有的。有关详细信息,请参阅 库和可见性
  • 标识符可以以字母或下划线(_)开头,后跟这些字符加数字的任意组合。
  • Dart可以使用表达式(具有运行时值)和 语句(不具有)。例如,条件表达式 condition ? expr1 : expr2的值为expr1expr2。将其与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的单词是与Dart 1.0发布后添加的异步支持相关的更新,有限的保留字。不能使用awaityield作为任何函数体中的标识符标记asyncasync*sync*
    表中的所有其他单词都是保留字,不能是标识符。

变量

这是创建变量并初始化它的示例:

var name = 'Bob' ; 

变量存储引用。名为name的变量指向String值为“Bob” 的对象。
name推断变量的类型String,但您可以通过指定它来更改该类型。如果对象不限于单一类型,请按照设计准则指定Objectdynamic类型 。

dynamic name = 'Bob' ; 

另一种选择是显式声明类型:

String name = 'Bob' ; 

注意: 此页面遵循 风格指南建议 使用var,而不是指定类型,用于局部变量。

默认值

未初始化的变量的初始值为null。即使是具有数字类型的变量最初也是null,因为数字 - 就像Dart中的其他所有都是对象。

int lineCount ; 
assert (lineCount == null );

注意:生产环境代码中将忽略assert() 调用。在开发期间, 除非条件为真,否则抛出异常。有关详细信息,请参阅断言

Final and const

如果您从未打算更改变量,请使用final或const代替var或替代类型。最终变量只能设置一次; const变量是编译时常量。(Const变量是隐式final的。)final的顶级或类变量在第一次使用时被初始化。
以下是创建和设置最终变量的示例:

final name = 'Bob'; // Without a type annotation
final String nickname = 'Bobby';

您无法更改最终变量的值:

name = 'Alice' ; //错误:最终变量只能设置一次。  

使用const为您要为变量的编译时间常数。如果const变量在类级别,请声明为static const。在声明变量的地方,将值设置为编译时常量,例如数字或字符串字面值,const变量或对常数进行算术运算的结果:

const bar = 1000000; // 压力单位 (dynes/cm2)
const double atm = 1.01325 * bar; // 标准大气压

该const关键字不只是声明常数变量。您还可以使用它来创建常量值,以及声明创建常量值的构造函数。任何变量都可以具有常量值。

var foo = const [];
final bar = const [];
const baz = []; //相当于`const []`  

您可以省略声明const的初始化表达式,如上所述。有关详细信息,请参阅不要冗余地使用constconst``baz
您可以更改非final,非const变量的值,即使它曾经有一个const值:

foo = [1, 2, 3]; // Was const []

您无法更改const变量的值:

baz = [ 42 ]; //错误:无法为常量变量赋值。  

有关使用const创建常量值的更多信息,请参阅 ListsMapsClasses

内置类型

Dart语言特别支持以下类型:

  • numbers
  • strings
  • booleans
  • lists (also known as arrays)
  • sets
  • maps
  • runes (for expressing Unicode characters in a string)
  • symbols
    您可以使用字面值初始化任何这些特殊类型的对象。例如,'this is a string'是一个字符串字面值,true是一个布尔字面值。
    因为Dart中的每个变量都引用一个对象 - 一个类的实例 - 您通常可以使用构造函数来初始化变量。一些内置类型有自己的构造函数。例如,您可以使用Map()构造函数来创建Map。

Numbers

Dart 数字有两种形式:
int
整数值不大于64位,具体取决于平台。在Dart VM上,值可以是-2 63到2 63 - 1.编译为JavaScript的Dart使用 JavaScript编号, 允许从-2 53到2 53 - 1的值。

double
64位(双精度)浮点数,由IEEE 754标准规定。
这两个intdouble的亚型num num类型支持基本的运算符,如+, - ,/和*,以及abs()ceil()floor()其他方法已在其中定义。(按位运算符,例如>>,在int类中定义。)如果num及其子类型没有您要查找的内容,则 dart:math库可能会定义。
整数是没有小数点的数字。以下是定义整型字面值的一些示例:

var x = 1;
var hex = 0xDEADBEEF;

如果数字包含小数,则为双精度数。以下是定义双精度字面值的一些示例:

var y = 1.1;
var exponents = 1.42e5;

从Dart 2.1开始,必要时整型字面值会自动转换为双精度数:

double z = 1; // Equivalent to double z = 1.0.

版本说明: 在Dart 2.1之前,在双精度数变量赋值为整型字面值是错误的。

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

// 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');

int类型具有传统的按位移位(<<,>>),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.";

您可以使用表达式将表达式的值放在字符串中 。如果表达式是标识符,则可以跳过{}。要获取与对象相对应的字符串,Dart会调用该对象的方法。${expression}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!');

注: 在==两个物体操作测试是否是等价的。如果两个字符串包含相同的代码单元序列,则它们是等效的。(这一点和java是不同的,java,String的==运算符,只有当地址一样,字面值一样,才能使相等的,dart String 的== 相当于java String的equal)

您可以使用相邻的字符串字面值或+ 运算符来连接字符串:

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.""";

您可以通过为其添加前缀来创建“raw”字符串r:

var s = r'In a raw string, not even \n gets special treatment.';

有关如何在字符串中表示Unicode字符的详细信息,请参阅Runes

字符串字面值是编译时常量,因为任何插值表达式是一个编译时常量,其值为null或数值,字符串或布尔值。

// These work in a const string.
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';

// These do NOT work in a const string.
var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = [1, 2, 3];

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

有关使用字符串的更多信息,请参阅 字符串和正则表达式

Booleans

为了表示布尔值,Dart具有一个名为的类型bool。只有两个对象具有bool类型:boolean 字面值 true和false,它们都是编译时常量。

Dart的类型安全意味着你不能使用像或 那样的代码,如下:

if (nonbooleanValue)
  assert (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

也许几乎每种编程语言中最常见的集合是数组或有序的对象组。在Dart中,数组是 List对象,因此大多数人只是将它们称为列表

Dart List文字看起来像JavaScript数组字面值。这是一个简单的Dart List:

var list = [1, 2, 3];

注意: Dart推断出list有类型List<int>。如果尝试将非整数对象添加到此List,则分析器或运行时会引发错误。有关更多信息,请阅读 类型推断。

List使用从零开始的索引,list.length - 1是最后一个元素的索引。您可以获得List的长度并像在JavaScript中一样引用List元素:

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

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

要创建一个编译时常量const的List,请在List文字前添加const

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

List类型有许多方便的方法来操作List。有关List的更多信息,请参阅泛型集合

Sets

Dart中的set是一组无序的独特item集合。对集合的Dart支持由set 字面值和Set类型提供。
这是一个简单的Dart集,使用set 字面值创建:

var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};

Dart推断出halogens具有该类型 Set<String>。如果您尝试向集合中添加错误类型的值,则分析器或运行时会引发错误。有关更多信息,请阅读 类型推断。

要创建一个空集,请使用{}前面带有类型参数,或者指定{}给类型的变量Set:

var names = <String>{};
// Set<String> names = {}; // This works, too.
// var names = {}; // Creates a map, not a set.

Set还是Map? Map字面值的语法类似于Set字面值的语法。由于Map的优先级靠前,因此{}默认为Map类型。如果您忘记了类型注释{}或它所分配的变量,则Dart会创建一个类型的对象Map<dynamic, dynamic>。

使用add()或addAll()方法将项添加到现有集:

var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);

使用.length得到的一组Set的数量:

var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);
assert(elements.length == 5);

要创建一个编译时常量const的集合,请在set literal之前添加const

final constantSet = const {
  'fluorine',
  'chlorine',
  'bromine',
  'iodine',
  'astatine',
};
// constantSet.add('helium'); // Uncommenting this causes an error.

有关集的更多信息,请参阅 泛型Set

Maps

通常,映射是关联键和值的对象。键和值都可以是任何类型的对象。每个只出现一次,但您可以多次使用相同的。Map的Dart支持由Map字面值和Map类型提供。

这里有几个简单的Dart Map例子,使用Map字面值创建:

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

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

Dart推断出gifts的类型 Map<String, String>以及nobleGases类型的 Dart Map<int, String>。如果您尝试将错误类型的值添加到任一映射,则分析器或运行时会引发错误。有关更多信息,请阅读 类型推断。

您可以使用Map构造函数创建相同的对象:

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

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

注意: 您可能想添加new而不仅仅是Map()。从Dart 2开始,new关键字是可选的。有关详细信息,请参阅使用构造函数

像在JavaScript中一样,将新的键值对添加到现有Map中:

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

以与在JavaScript中相同的方式从Map中检索值:

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

如果您查找不在Map中的键,则会得到null作为回报:

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

使用.length得到的映射中的键值对的数量:

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

要创建一个编译时常量const的Map,请在Map字面值之前添加const
有关Map的更多信息,请参阅 泛型Map

Runes

在Dart中,Runes是字符串的UTF-32表达。
Unicode为世界上所有书写系统中使用的每个字母,数字和符号定义唯一的数值。由于Dart字符串是UTF-16代码单元的序列,因此在字符串中表示32位Unicode值需要特殊语法。
表达Unicode代码点的常用方法是 \uXXXX,XXXX是4位十六进制值。例如,心脏角色(♥)是\u2665。要指定多于或少于4个十六进制数字,请将值放在大括号中。例如,笑的表情符号(😆)是\u{1f600}。
字符串 类有几个属性,你可以用它来提取Runes信息。在codeUnitAtcodeUnit属性返回16位编码单元。使用该runes属性获取字符串的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));
}

注意: 使用列表操作操作符文时要小心。这种方法很容易使字符串解体,具体取决于特定的语言,字符集和操作。有关更多信息,请参阅 如何在Dart中反转字符串?在Stack Overflow上。

Symbols

Symbol对象表示在Dart程序中声明的运算符或标识符。您可能永远不需要使用符号,但它们对于按名称引用标识符的API非常有用,因为缩小会更改标识符名称而不会更改标识符符号。要获取标识符的符号,请使用符号文字,它只是#后跟标识符:(这一段实在不会翻译了- -)
要获取标识符的符号,请使用符号文字, #后面跟着标识符:

 #radix
 #bar

Symbols文字是编译时常量。

Functions

Dart是一种真正的面向对象语言,因此即使是函数也是对象并且具有类型Function。 这意味着函数可以分配给变量或作为参数传递给其他函数。您也可以调用Dart类的实例,就好像它是一个函数一样。有关详细信息,请参阅Callable classes

以下是实现函数的示例:

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

虽然Effective Dart建议 为公共API声明返回值类型,但如果省略类型,该函数仍然有效:

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

对于只包含一个表达式的函数,可以使用简写语法:

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

这个 => 是{ return expr; }的速记 . 。有时将=> 表示法称为箭头语法。
只有表达式(而不是语句)能够出现在箭头(=>)和分号(;)之间。例如,您不能在其中放置if语句,但可以使用条件表达式
函数可以有两种类型的参数:必需和可选。首先列出所需参数,然后列出任何可选参数。命名的可选参数也可以标记为@required。有关详细信息,请参阅下一节。

可选参数

可选参数可以是位置参数,也可以是名称参数,但不能同时包含

可选的名称参数

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

enableFlags (bold :true ,hidden :false );  

定义函数时,用于 指定名称参数:{param1, param2, …}

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

Flutter实例创建表达式可能变得复杂,因此窗口小部件构造函数仅使用命名参数。这使得实例创建表达式更易于阅读。
您可以使用@required在任何Dart代码(不仅仅是Flutter)中注释命名参数, 以指示它是必需参数。例如:

const Scrollbar({Key key, @required Widget child})

当一个Scrollbar构造,当必要参数缺少时,编译器会报错。
requaredmate包中定义。可以package:meta/meta.dart直接导入 ,也可以导入另一个导出的包 meta,例如Flutter package:flutter/material.dart

可选的位置参数

包装一组函数参数将[]它们标记为可选的位置参数:

String say(String from, String msg, [String device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}

这是一个在没有可选参数的情况下调用此函数的示例:

assert(say('Bob', 'Howdy') == 'Bob says Howdy');

以下是使用第三个参数调用此函数的示例:

assert(say('Bob', 'Howdy', 'smoke signal') ==
    'Bob says Howdy with a smoke signal');

默认参数值
您的函数可用于=定义命名和位置参数的默认值。默认值必须是编译时常量。如果未提供默认值,则默认值为null。
以下是为命名参数设置默认值的示例:

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

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

弃用注释: 旧代码可能使用冒号(:)而不是= 设置命名参数的默认值。原因是最初只:支持命名参数。该支持可能已被弃用,因此我们建议您 使用=指定默认值。

下一个示例显示如何设置位置参数的默认值:

String say(String from, String msg,
    [String device = 'carrier pigeon', 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');

您还可以将List或Map作为默认值传递。以下示例定义了一个函数,该函数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');
}

main()函数

每个应用程序都必须具有顶级main()功能,该功能用作应用程序的入口点。该main()函数返回void并具有List<String>参数的可选参数。
以下main()是Web应用程序功能的示例:

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

注意:..前面代码中 的语法称为级联。使用级联,您可以对单个对象的成员执行多个操作。

以下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库来定义和解析命令行参数。

Function 作为第一类对象的功能

您可以将函数作为参数传递给另一个函数。例如:

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 !!!');

此示例使用了匿名函数。更多关于下一节的内容。

匿名函数

大多数函数都被命名,例如main()或printElement()。您还可以创建一个名为匿名函数的无名函数,有时也可以创建一个lambda或闭包。您可以为变量分配匿名函数,以便例如可以在集合中添加或删除它。

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

后面的代码块包含函数的主体:

([[Type] param1[, …]]) {
codeBlock;
};

以下示例使用无类型参数item定义匿名函数,为List中的每个项调用的函数将打印一个包含指定索引处的值的字符串。

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

如果函数只包含一个语句,则可以使用箭头表示法缩短它。

list.forEach(
    (item) => print('${list.indexOf(item)}: $item'));

作用域

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()每个级别的变量,一直到顶级。

闭包

一个闭包是能够访问在其作用域的变量的函数的对象,即使当函数用于其原来的范围之外。
函数可以关闭周围范围中定义的变量。在以下示例中,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);
}

测试函数是否相等

以下是测试顶级函数,静态方法和实例方法的相等性的示例:

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

返回值

foo() {}
assert(foo() == null);

运算符

Dart定义下表中显示的运算符。您可以覆盖许多运算符,如在 可重写运算符

描述 运算符
一元后缀 expr++ expr-- () [] . ?.
一元前缀 -expr !expr ~expr ++expr --expr
* / % ~/
添加 + -
位移 << >> >>>
按位与 &
按位异或 ^
按位或 |
大小和类型测试 >= > <= < as is is!
相等性 == !=
逻辑与 &&
逻辑或 ||
非空 ??
?表达式 expr1 ? expr2 : expr3
级联 ..
再运算 = *= /= += -= &= ^= etc.

警告: 运算符优先级是Dart解析器行为。有关明确的规则,请参阅Dart语言规范中的语法

使用运算符时,可以创建表达式。以下是运算符表达式的一些示例:

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

运算符表中,每个运算符的优先级高于其后的行中的运算符。例如,乘法运算符的%优先级高于(因此之前执行)等于运算符==,它的优先级高于逻辑AND运算符&&。该优先级意味着以下两行代码执行相同的方式:

// Parentheses improve readability.
if ((n % i == 0) && (d % i == 0)) ...

// Harder to read, but equivalent.
if (n % i == 0 && d % i == 0) ...

警告: 对于处理两个操作数的运算符,最左边的操作数确定使用哪个对象的运算符。例如,如果您有Vector对象和Point对象,则aVector + aPoint使用Vector对象的+。

算术运算符

Dart支持通常的算术运算符,如下表所示。

Operator Meaning
+
-expr 负号
* 乘法
/ 除法
~/ 整除
% 获取整数除法的余数(模数)

例:

assert(2 + 3 == 5);
assert(2 - 3 == -1);
assert(2 * 3 == 6);
assert(5 / 2 == 2.5); // Result is a double
assert(5 ~/ 2 == 2); // Result is an int
assert(5 % 2 == 1); // Remainder

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

Dart还支持前缀和后缀增量和减量运算符。

操作者 含义
++var var = var + 1(表达式值是var + 1)
var++ var = var + 1(表达式值是var)
--var var = var – 1(表达式值是var – 1)
var-- var = var – 1(表达式值是var)
var a, b;

a = 0;
b = ++a; // Increment a before b gets its value.
assert(a == b); // 1 == 1

a = 0;
b = a++; // Increment a AFTER b gets its value.
assert(a != b); // 1 != 0

a = 0;
b = --a; // Decrement a before b gets its value.
assert(a == b); // -1 == -1

a = 0;
b = a--; // Decrement a AFTER b gets its value.
assert(a != b); // -1 != 0

相等和关系运算符

下表列出了相等运算符和关系运算符的含义。

操作者 含义
== 等于; 见下面的讨论
!= 不相等
> 比...更棒
< 少于
>= 大于或等于
<= 小于或等于

要测试两个对象x和y是否表示相同的事物,请使用 ==运算符。(在极少数情况下,您需要知道两个对象是否是完全相同的对象,请使用相同的() 函数。)以下是==运算符的工作方式:

  1. 如果xy为null,则如果两者都为null则返回true;如果只有一个为null,则返回false。

  2. 返回方法调用的结果 。(这是正确的,运算符,例如在第一个操作数上调用的方法。您甚至可以覆盖许多运算符,包括,正如您在Overridable运算符中看到的那样 。)*x*.==(*y*)``==``==

这是使用每个相等和关系运算符的示例:
assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);

类型检测运算符

使用as,is和is!运算符可以方便地在运行时检查类型。

操作者 含义
as Typecast(也用于指定库前缀)
is 如果对象具有指定的类型,则为True
is! 如果对象具有指定的类型,则返回false

obj is T如果obj实现了指定的接口,则结果为true T。例如,obj is Object总是如此。
使用as运算符将对象强制转换为特定类型。通常,您应该使用它作为is对使用该对象的表达式后跟对象的测试的简写。例如,请考虑以下代码:

if (emp is Person) {
  // Type check
  emp.firstName = 'Bob';
}

您可以使用as运算符缩短代码:

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

注意: 代码不相同。如果emp为null或不是Person,则第一个示例(with is)不执行任何操作; 第二个(带as)抛出一个异常。

赋值操作符

如您所见,您可以使用=运算符分配值。要仅在变量为null时分配,请使用??=运算符,如下,如果为null就执行f

// Assign value to a
a = value;
// Assign value to b if b is null; otherwise, b stays the same
b ??= value;

复合赋值运算符,例如+=将操作与赋值组合在一起。

= –= /= %= >>= ^=
+= *= ~/= <<= &= |=

以下是复合赋值运算符的工作原理:

复合赋值 等价表达
对于运营商op: a op= b a = a op b
例: a += b a = a + b

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

var a = 2; // Assign using =
a *= 3; // Assign and multiply: a = a * 3
assert(a == 6);

逻辑运算符

您可以使用逻辑运算符反转或组合布尔表达式

操作者 含义
!expr 反转以下表达式(将false更改为true,反之亦然)
|| 逻辑或
&& 逻辑AND

以下是使用逻辑运算符的示例:

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

按位和移位运算符

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

操作者 含义
&
|
^ 异或
~expr 一元逐位补码(0s变为1s; 1s变为0s)
<< 向左位移
>> 向右位移

这是使用按位和移位运算符的示例:

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

如果condition为true,则计算expr1(并返回其值); 否则,计算并返回expr2的值。

`expr1 ?? expr2`

如果expr1为非null,则返回其值; 否则,计算并返回expr2的值。
当您需要根据布尔表达式分配值时,请考虑使用?:

var visibility = isPublic ?'public' :'private' ;   

如果布尔表达式测试为null,请考虑使用??。

String playerName(String name) => name ?? 'Guest'; 

前面的例子至少可以用其他两种方式编写,但不够简洁:

// Slightly longer version uses ?: operator.
String playerName(String name) => name != null ? name : 'Guest';

// Very long version uses if-else statement.
String playerName(String name) {
  if (name != null) {
    return name;
  } else {
    return 'Guest';
  }
}

级联符号..

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

请考虑以下代码:

querySelector('#confirm') // Get an object.
  ..text = 'Confirm' // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));

第一个方法调用,querySelector()返回一个选择器对象。级联表示法后面的代码对此选择器对象进行操作,忽略可能返回的任何后续值。
前面的例子相当于:

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

您也可以嵌套您的级联。例如:

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

小心在返回实际对象的函数上构造级联。例如,以下代码失败:

var sb = StringBuffer();
sb.write('foo')
  ..write('bar'); // Error: method 'write' isn't defined for 'void'.
//返回为void的不能再构造级联
.setTitle
.setView
.setIcon

该sb.write()调用无返回值,不能构造void的级联。

其他运算符

在其他示例中,您会看到了大多数剩余的运算符:

操作者 名称 含义
() 功能应用 表示函数调用
[] 列表访问 引用列表中指定索引处的值
. 成员访问 指表达式的属性; 示例:从表达式中foo.bar选择属性barfoo
?. 有条件的成员访问权限 比如.,但最左边的操作数可以为null; 示例:从表达式中foo?.bar选择属性bar,foo除非foo为null(在这种情况下,值为foo?.barnull)

控制流程语句

您可以使用以下任一方法控制Dart代码的流程:

  • if 和 else
  • for 循环
  • while和do- while循环
  • break 和 continue
  • switch 和 case
  • assert
    您还可以使用try-catch和影响控制流throw,如异常中所述

If and else

Dart支持if带有可选else语句的语句,如下一个示例所示。另见条件表达式

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

您可以使用标准for循环进行迭代。例如:

var message = 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。相反,在js里会打印出2个2。
如果要迭代的对象是Iterable,则可以使用 forEach()方法。forEach()如果您不需要知道当前的迭代计数器,则使用是一个不错的选择:

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

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

var collection = [0, 1, 2];
for (var x in collection) {
  print(x); // 0 1 2
}
While and do-while

一个while循环在执行循环内的逻辑之前,先计算表达式是否符合条件:

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

一个while循环先计算表达式是否符合条件,然后执行循环内的逻辑:

do {
  printLine();
} while (!atEndOfPage());
Break and 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();
}

如果您使用Iterable(如列表或集合),则可能会以不同的方式编写该示例 :

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

Dart中的switch语句支持整数,字符串或,使用==来比较的编译时常量。比较对象必须都是同一个类的实例(而不是其任何子类型),并且该类不能覆盖==枚举类型switch语句也支持。

注意: Dart中的Switch语句适用于有限的情况,例如interpreters 或scanners。

每个非空case子句break通常以语句结束。其他有效的方式来结束一个非空的case条目是continue, throw或return。
default当没有case子句匹配时,使用子句执行代码:

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

以下示例省略break了case子句中的语句,从而产生错误(如果当前case拥有语句,不写break的话,会报错):

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

  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如果布尔条件为false,断言语句中断正常执行,以下为例:

// 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'));

注意: 断言语句对生产代码没有影响; 他们只是为了发展。Flutter在调试模式下启用断言 仅限开发的工具(如dartdevc) 通常默认支持断言。一些工具,如dartdart2js, 通过命令行标志支持断言:--enable-asserts

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

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

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

Exceptions

你的Dart代码可以抛出和捕捉异常。异常是错误,表示发生了意外的事情。如果没有捕获到异常,引发异常的隔离会挂起,通常隔离及其程序会终止。

与Java相比,Dart的所有异常都是未经检查的异常。方法不会声明它们可能引发的异常,并且您不需要捕获任何异常。

Dart提供了ExceptionError 类型,以及许多预定义的子类型。当然,您可以定义自己的例外情况。但是,Dart程序可以抛出任何非null对象 - 不仅仅是Exception和Error对象 - 作为异常。

Throw

以下是抛出或引发异常的示例:

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

你也可以抛出任意对象:

throw 'Out of llamas!';

注意:生产质量代码通常会抛出实现错误异常的类型 。
因为抛出异常是一个表达式,所以可以在=>语句中以及允许表达式的任何其他地方抛出异常:

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

Catch

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

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

正如上面的代码所示,您可以使用oncatch或两者兼而有之。使用on时需要指定异常类型。使用catch时,你的异常处理程序需要异常对象。

要处理可能抛出多种类型异常的代码,可以指定多个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');
}

正如上面的代码所示,您可以使用oncatch或两者兼而有之。使用on时需要指定异常类型。使用catch时,你的异常处理程序需要异常对象。

您可以指定一个或两个参数catch()。第一个是抛出的异常,第二个是堆栈跟踪(StackTrace对象)。

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

要部分处理异常,同时允许它传播,请使用rethrow关键字。

void misbehave() {
  try {
    dynamic foo = true;
    print(foo++); // Runtime error
  } 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.
}

阅读 dart之旅的Exception部分,了解更多相关信息 。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343