【翻译】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.
}
复制代码
  • //Define a function.javascript

    单行注释块.Dart还支持多行注释块/* */html

  • intjava

    一个类型,其余常见的list,String,boolgit

  • 42github

    常量做为一个数字web

  • print()正则表达式

    一个便利的方法显示输出算法

  • '....'("....")express

    字符串文字编程

  • $variableName(or $expresss)

    接受常量或者表达式,在字符串中包含变量或者表达式

  • main()

    main()是必须的,特殊的,顶级的函数,做为程序执行的入口

  • var

    一个方法声明变量没有指定他的类型

备注:这个网站代码遵循着 dart风格指南(Dart style guide)

重要的概念

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

  • 全部能够放入变量中的都是一个对象,每个对象都是类的实例。每个数字,函数,甚至null都是一个对象。全部的对象都继承于对象类。

  • 虽然Dart语言是一个强类型的,可是准肯定义类是可选的。由于Dart能够推断类型。在上面的代码中,number被推出是int类型,当你预料不到这个类型时,使用这个特殊的类dynamic

  • Dart支持范类型,像List<int>(整数列表)或者List<dynamic>(任何类型的列表)

  • Dart支持顶级的函数(如mian()),也支持函数关联在类或者对象上(分别时静态方法或者实例方法),你也能够建立一个函数在一个函数内(嵌套函数或局部函数)

  • 不像javaDart没有关键字 public, protected,和 private,若是一个标识符开始是下划线(_),对这个库而言它是私有的。

  • 标识符开始于一个字母或者下划线(_),接下来能够是任何字母或者数字的组合。

  • Dart语言同时具备表达式(具备运行时值)和语句(不具备运行时值),例如条件表达式condition ? expr1 : expr2 其值不是expr1就是expr2,而if-else 则没有。一个语句同时包含一个或者多个表达式,但一个表达式不能直接包含语句。

    runtime values直译来是运行时值。

    if (expression) { statement(s) } else { statement(s) }
    //这个语句没有runtime values,也就是不能复制给变量。
    //而改写
    var a=condition ? expr1 : expr2;
    // ----------------------
    var a;
    if(condition){
        a=expr1;//只能在这里写复制表达式
    } else {
        a=expr2;//只能在这里写复制表达式
    }
    复制代码
  • Dart工具能够报告出两种问题:warning(警告)和errors(错误)。警告仅仅代表你的代码可能有问题,但他们不阻止代码的执行,错误是编译错误,也能够是运行时出错,编译错误将没法运行,运行时错误,将在程序执行时发生异常

关键字

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代码,这些关键词是有效的在大多数地方,可是他们不能用于类名或者类型名,也不能作import 前缀

  • 上标3是在Dart1.0后加的,用于支持异步的特性,你不能使用await或者yield做为一个标识符在任何一个函数体使用asyncasync或者sync**内。

表中的全部其余单词都是保留字,不能是标识符。

变量

建立并初始化一个变量

var name = 'Bob';
复制代码

变量是一个引用。这个被命名为name的变量包含了一个对String对象的引用,该对象的值为“Bob”。

能够看出标识符为name的变量是一个String(字符串),可是你也能够明确指定它的类型。若是一个对象不局限于单一类型,请按设计指南(design guidelines指定Object或者dynamic类型

dyname name = 'Bob';
复制代码

明确的声明这个变量

Sting name = 'Bob';
复制代码

这个页面遵循 Dart语言风格指南推荐(style guide recommendation)。对局部变量使用var,而不是明确指定它的类型

默认值

未初始化的变量有一个默认的值null。甚至一个数字类型的变量也被初始化为null,由于Dart中数字和其余任何类型同样都是对象。

int lineCount;
assert(lineCount==null);  
复制代码

备注:assert(condition)的只会在程序开发期间生效,若是condition是false将会抛出一个错误。正式的代码将会忽略assert()的调用。

finalconst

若是你从不想改变一个变量,使用final或者constfinal变量只能被设置一次,const变量编译运行时不变。(const变量也是final。)顶层的final变量或者类中的final在第一次使用的时候才进行初始化。

实例变量只能使用final,而不能使用const。final 实例变量必须初始化在final变量声明处(构造体)以前,可使用构造函数传参或者使用构造函数的 初始化列表initializer list来初始化。

建立和设置final变量的例子

final name = 'Bob';   
final String nickname = 'Bobby';
复制代码

你不能该改变final的变量的值

name = 'Alice';     
// Error: a final variable can only be set once.
复制代码

使用const能够定义编译时常量(这里是compile-time constants:编译时常量)。若是const变量是claas级别的,标记为static const。在变量声明处,将能够将数字或者字符字面量、const变量或对常数进行算数的结果设置为编译时常量

const bar = 1000000; // Unit of pressure (dynes/cm2)
const double atm = 1.01325 * bar; // Standard atmosphere
复制代码

const关键字不只仅用于声明常量(字面量、数字或者cosnt变量)变量。还可使用它来建立常量值,以及声明建立常量值的构造器。任何变量均可以有一个常量。

var foo = const [];
final bar = const [];
const baz = []; //等效于 `const []`
复制代码

像上面的baz同样,初始化表达式上省略const。详细不要使用冗余的使用const(DON’T use const redundantly)

你能够改变非final、非const的变量,即便它有一个const的值:

//foo声明处在上一个例子中
foo = [1, 2, 3]; // Was const [] 
复制代码

你没法更改const声明的变量

baz = [42]; // Error: Constant variables can't be assigned a value.
复制代码

dart2.5开始,你能够定义const使用 类型检测和转换(type checks and casts)isand)、集合if和集合for(使用if和for构建集合的语句)、拓展运算符(......?):

// 如下在dart2.5中有效
const Object i = 3; //i是具备int值的const对象
const list = [i as int]; // 使用类型转换。
const map = {if (i is int) i: "int"}; // 使用is和collection if.
const set = {if (list is List<int>) ...list}; // ...拓展运算符.
复制代码

有关const的更多信息,参阅List,Maps和Classes。

内置类型

Dart 语言对如下类型提供了特殊的支持:

  • number
  • string
  • boolean
  • lists(也被称之为arrays)
  • maps
  • runes(用于在字符表示Unicode编码)
  • symbols.

你可使用字面量(字面量就是输给编程语言的文字)来初始化这些特殊的类型。例如,'this is a string'是一个string的字面量,true是一个boolean的字面量

由于在dart中每个变量都是一个对象即一个实例化的类。因此你可使用构造函数去初始化。一些内置的类型有本身的构造函数。例如你可使用Map()构造函数去建立map

Nubers

Dart中只有两种:

int

整形的值不大于64位,具体取决于平台。在Dart VM,值的范围为-263到263,编译成JavaScript的Dart,值的范围为 -253到253

double

64位(双倍精度)浮点数,遵行IEEE 754的标准的规定。

int和double都是num的子类。num类型包括基础的运算符,例如+,-,/和*,你也能够找到abs(),ceil(),和floor()以及其余的方法(按位运算符,如>>,是定义在int类的)若是num和他的子类没有你要的内容,dart:math可能能帮到你。

整形数字没有小数。

var x = 1;
var hex = 0xDEADBEEF;//16进制
复制代码

若是一个数包括小数它是double。

var y = 1.1;
var exponents = 1.142e5;
复制代码

从Dart2.1开始,必要时,整形变量自动转化为doubles。

double z = 1; // 等效于double z = 1.0.
复制代码

版本:在Dart2.1以前,它是错误的。使用整形的变量储存double类型的自卖能量

类型的转化,string转化为number,number转化为string

// 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
复制代码

number字面量是编译时的常量,许多数学表达式只要操做的数是编译时常量,则结果也是编译时常量。

const msPerSecond = 1000;
const secondsUntilRetry = 5;
const msUntilRetry = secondsUntilRetry * msPerSecond;
复制代码

string

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.";
复制代码

你能够经过${express}把表达式的值放入字符串中,若是表达式只是是标识符,你须要省略{}。为了获取对象相对应的字符串,dart调用该对象的方法toString()方法(如:print([1,2])先调用[1,2].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, not even \n gets special treatment.';
复制代码

参考 Runes 来了解如何在字符串 中表达 Unicode 字符。

字符串中只要插值表达式是常量(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类型,truefalse,二者都是常量。

dart的类型安全意味着,你不能使用if(nonbolleanValue)或者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。因此咱们一般都称之为lists。

dart语言的数组很像JavaScript数组。这里有一些简单的例子。

var list  = [1,2,3];
复制代码

dart 推断上面的list是list<int>类型。若是你尝试添加一个非整数类型的对象到这个list中,程序分析器或者运行环境会抛出一个错误。想要更多信息,阅读type inference.

列表使用从0开始的索引。0为list的第一个元素,list.length-1是list最后一个元素。你能够像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.
复制代码

dart2.3引进拓展运算符(spread operator)(...)和空感知扩展运算符(...?) ,提供一个简明的方法在容器中插入大量的元素。

例如,你可使用拓展运算符(...)插入列表中所有的元素到另一个列表中。

var list = [1, 2, 3];
var list2 = [0, ...list];
assert(list2.length == 4);
复制代码

若是拓展运算符右边的表达式为空,你使用空感知拓展运算符(...?)避免异常:

var list;
var list2 = [0, ...?list];
assert(list2.length == 1);
复制代码

点击spread operator获取拓展运算符更多的用法和例子

Dart2.3也引入了 collection if 和**collection for ** ,你可使用条件语句(if)和循环语句(for)在容器中添加元素。

collection if 去建立三个或四个元素

var nav = [
  'Home',
  'Furniture',
  'Plants',
  if (promoActive) 'Outlet'
];//promoActive是true 为四个元素,反之为三个元素。
复制代码

collection for去添加大量的元素到列表中。

var listOfInts = [1, 2, 3];
var listOfStrings = [
  '#0',
  for (var i in listOfInts) '#$i'
];
assert(listOfStrings[1] == '#1');
复制代码

点击 control flow collections proposal.获取更多collection if和 collection for的例子。

List类有不少操做列表方便的方法,点击 范类(Generics)集合(Collections)获取更多的信息。

Sets

在dart中set是无序无重复元素的集,set字面量和 Set类型提供sets的支持。

虽然Set是核心库里的内容,但set是dart2.2才被引入的。

经过set字面量建立sets

var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
复制代码

dart判断上面的halogens变量是Set。若是你尝试添加一个错误的类型至set中,程序分析器或者运行环境会抛出一个错误。想要更多信息,阅读type inference.

使用类型后加{}或者将{}分配给Set类型的变量,建立空的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);
复制代码

在set字面量前使用const建立set常量。

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

Dart2.3起,sets拓展运算符(......?)和collection ifs 和 fors,就像lists同样。点击 spread operator proposalcontrol flow collections proposal,获取更多拓展运算符和collection 用法和信息。

点击 Generics and Sets或者更多sets的信息。

Maps

一般,map是关联键和值的对象。键和值均可以是任何类型的对象。每一个键只能出现一次,而一个值能够重复屡次。Dart 经过 map 字面量 和 Map 类型支持 map。

下面是一些建立简单 map 的示例:

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

var nobleGases = {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};
复制代码

dart判断giftsMap<String, String> nobleGasesMap<int, String>。若是你尝试添加一个错误的类型至二者的map中,程序分析器或者运行环境会抛出一个错误。想要更多信息,阅读type inference.

你能够建立同样的对象经过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()而不是Map()。dart 2起,new关键字是可选的。点击 Using constructors.获取更多的信息。

添加一个键值对到一个现存的map中和JavaScript方法同样。

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

获取一个值的方法也和JavaScript一致

var gifts = {'first': 'partridge'};
assert(gifts['first'] == 'partridge');
复制代码

若是你查找的键不在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字面量前使用const建立map编译时的常量。

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

// constantMap[2] = 'Helium'; // Uncommenting this causes an error.
复制代码

Dart2.3起,maps支持拓展运算符(......?)和collection ifs 和 fors,就像lists同样。点击 spread operator proposalcontrol flow collections proposal.,获取更多拓展运算符和collection 用法和信息。

点击 Generics and Maps或者更多maps的信息。

Runes

在Dart中,runes表明字符串的utf-32 code points。

Unicode 规定一个惟一的数字标量对世界上全部书写系统的每个单词,数字和符号。由于dart字符串是由UTF-16编码单元组成的序列,表达32位的Unicode值须要用到特殊的语法。

一般的方法表达Unicode code points 是 \uXXXX,XXXX是4位十六进制的值。 例如,心形符号 (♥) 是 \u2665。 对于非 4 个数值的状况, 把编码值放到大括号中便可。 例如,笑脸 emoji (😆) 是 \u{1f600}

String 类型有有一系列属性你能够提取rune的信息。codeUnitAtcodeUnit属性返回一个16位的代码单元。使用 runes 属性来获取字符串的 runes 信息。

下面是一些例子

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));
}
//👏
//[55357, 56399]
//[128079]
//♥ 😅 😎 👻 🖖 👍
复制代码

使用 list 操做 runes 的时候请当心。你所操做的特定的语种、字符集和操做方式,可能致使你的字符串出问题。 更多信息参考 Stack Overflow 上的一个问题: 如何在 Dart 中反转一个字符串?

Symbols

Symbol 对象表明在Dart程序中声明的运算符或者操做符。你可能历来不会用到symbols,可是symbols对于名称引用的标识符的api时极有价值的,由于缩小标识符的名字不会改变标识符的symbols。

使用 # 后跟标识符,获得一个symbol字面量

#radix
#bar
复制代码

symbol字面量是编译时的常量

函数

dart是面向对象的语言, Function.是一个对象,而且具备一个类型。这个意味着函数能够分配给变量,也能够做为参数传给其余函数。你也能像函数同样调用类的实例。点击 Callable classes.得到更多信息。

这里是实现函数的例子

bool isNoble(int atomicNumber) {//atomicNumber原子序数
  return _nobleGases[atomicNumber] != null;   //_nobleGases稀有气体
}
复制代码

虽然Effective Dart推荐公共APIS有类型注释,可是忽略类型函数仍然是有效的。

isNoble(atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}
复制代码

对于只有一个表达式的式子,可使用简略语法

bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
复制代码

=> expr语法是{ return expr; }.简略的表达式。 =>符号也被称做箭头函数。

仅仅一个表达式(非语句)能够放在箭头(=>)和分号 (;)之间。你不能将一个条件语句 (if statement)放到这里,可是能够放条件表达式(conditional expression.)

函数能够有两个参数:必选的和可选的。首先列出的是必选参数,接着是可选的参数。可选参数能够是命名可选参数或者位置可选参数。

注意:一些api尤为是 Flutter 小部件构造函数只使用命名参数,即便对于强制参数也是如此。有关详细信息,请参见下一节。
复制代码

可选参数Optional parameters

可选参数能够是位置参数,也能够是命名参数。但不能是二者都是。

可选命名参数Optional named parameters

可选命名参数。调用函数时,你可使用paramName: value指定参数。

enableFlags(bold: true, hidden: false);
复制代码

当定义一个函数时,使用{param1, param2, …}指定命名参数。

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

虽然命名参数时是可选的,你可使用 @required来注释一个命名可选的参数是强制的,用户必须为此参数提供值。

const Scrollbar({Key key, @required Widget child})
复制代码

当构建Scrollbar时,分析器会在缺乏child参数时提示错误。但运行时不会抛出错误。(这个是Flutter中的内容)

要使用@required依赖于 meta库,请导入package:meta/meta.dart

位置可选参数Positional parameters

在函数参数内使用[]包裹的参数,是位置可选参数。

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

不写位置参数,调用这个函数

assert(say('Bob', 'Howdy') == 'Bob says Howdy');
复制代码

写位置参数,调用这个函数

assert(say('Bob', 'Howdy', 'smoke signal') ==
    'Bob says Howdy with a smoke signal');
复制代码

设置参数的默认值Default parameter values

你的函数可使用=命名可选参数位置可选参数添加默认值。默认值必须是常量。若是没有定义默认值,这个默认值为null

这里有一些例子为命名可选参数设置默认参数。

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

// bold will be true; hidden will be false.
enableFlags(bold: true);
复制代码

老代码可能会使用冒号(:)而不是(=)设置默认值。缘由是,原代码最初只有:是支持的。:设置命名参数默认值可能在之后版本不能使用,因此咱们推荐你使用=设置默认值。

设置默认值为位置参数

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(),并为listgifts设置了默认值

void doStuff(
    {List<int> list = const [1, 2, 3],
    Map<String, String> gifts = const {
      'first': 'paper',
      'second': 'cotton',
      'third': 'leather'
    }}) {
  print('list: $list');
  print('gifts: $gifts');
}
复制代码

主函数The main() function

每个程序必须有一个顶层函数main(),做为程序的入口。main()返回void和有一个可选的参数 List<String>

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

在上面的代码中..语法叫作[cascade](#Cascade notation (..)).使用级联,您能够对单个对象的成员执行多个操做。

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 as first-class objects

你能够将函数做为参数做为一个参数传给另外一个参数。

void printElement(int element) {
  print(element);
}

var list = [1, 2, 3];

// 将printElement做为参数传递。
list.forEach(printElement);
复制代码

你也能够将函数分配给一个变量

var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
复制代码

这个例子使用了一个匿名函数。下一节将介绍更多信息。

匿名函数Anonymous functions

绝大数函数是命名函数,如main()或者printElement(),你也能够建立一个不须要名字的函数,称之为匿名函数。有时是lambda或者闭包函数。你分配匿名函数给一个变量。例如,你能够从一个集合中添加或者删它。

匿名函数看起来像明明函数——零个或者多个参数,括号内用逗号分隔和可选类型注释分隔。

下面注释块包含函数的主体

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

用无类型的参数item. 定义匿名函数。这个函数,为列表中的每一个项调用,打印一个字符串,该字符串包含指定索引处的值。

var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});
//0: apples
//1: bananas
//2: oranges
复制代码

若是函数仅包含一个语句,你可使用箭头函数简化。

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

静态做用域Lexical scope

Dart 是静态做用域语言,变量的做用域是由变量的位置肯定的。您能够跟随大括号向外查看变量是否在做用域中。

bool topLevel = true;

void main() {
  var insideMain = true;

  void myFunction() {
    var insideFunction = true;

    void nestedFunction() {
      var insideNestedFunction = true;

      assert(topLevel);
      assert(insideMain);
      assert(insideFunction);
      assert(insideNestedFunction);
    }
  }
}
复制代码

注意 nestedFunction() 如何使用每一个级别的变量。

闭包 Lexical closures

闭包是一个函数对象,无论在何处调用,该对象均可以访问其做用域内的变量。

函数能够在周围定义处封闭变量,在如下示例中,makeAdder()得到变量addBy。不管返回的函数在哪里,它都会保留addBy的值。

/// 返回一个将[addBy]添加到的函数
/// 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);
}
复制代码

相等性测试函数Testing functions for equality

下面是测试顶级函数、静态方法和实例方法是否相等的示例

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

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

void main() {
  var x;

  // Comparing top-level functions.
  x = foo;
  assert(foo == x);

  // Comparing static methods.
  x = A.bar;
  assert(A.bar == x);

  // Comparing instance methods.
  var v = A(); // Instance #1 of A
  var w = A(); // Instance #2 of A
  var y = w;
  x = w.baz;

  // These closures refer to the same instance (#2),
  // so they're equal.
  assert(y.baz == x);

  // These closures refer to different instances,
  // so they're unequal.
  assert(v.baz != w.baz);
}
复制代码

返回值Return values

全部的函数都返回一个值,若是没有返回一个指定的值,这个表达式将隐式地附加到函数体return null

foo() {}

assert(foo() == null);
复制代码

Operators

在接下的表格中,你能够看到Dart定义的运算符。如[运算符重写操做Overridable operators](overridable operators).所示你能够重写这些运算符。

Description Operator
后缀unary postfix expr++ expr-- () [] . ?.
前缀unary prefix -expr !expr ~expr ++expr --expr
乘除multiplicative * / % ~/
添加additive + -
位移shift << >> >>>
按位与bitwise AND &
按位异或bitwise XOR ^
按位或bitwise OR |
关系运算符relational and type test >= > <= < as is is!
相等equality == !=
逻辑与logical AND &&
逻辑或logical OR ||
判断空if null ??
控制conditional expr1 ? expr2 : expr3
级联cascade ..
赋值assignment = *= /= += -= &= ^=

警告:运算符优先级是Dart解析器行为的近似值。要得到明确的答案,请参阅 Dart语言规范中的语法.

当你使用运算符,你建立一个表达式。这里是一些例子运算符的表达式。

a++
a + b
a = b
a == b
c ? a : b
a is T
复制代码

表格中,每一个操做符的优先级都高于其后行的操做符。例如,multiplicative %运算符的优先级高于equality ==(所以multiplicative 在以前进行)。equality ==的优先级高于logical AND operator &&,所以下面两行代码按照相同的方式运行

// 括号提升可读性
if ((n % i == 0) && (d % i == 0)) ...

// 等效
if (n % i == 0 && d % i == 0) ...
复制代码

警告:对于一个二元运算符,使用左边的运算符操做方式。例如,你有一个Vector 的对象和一个Point 的对象相加aVector + aPoint ,使用的是Vector 版本的 +(重写运算符时)

Arithmetic operators

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还支持前缀和后缀的自增自减运算符

Operator Meaning
++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; // a先自增再赋值给b
assert(a == b); // 1 == 1

a = 0;
b = a++; // a先赋值给b后自增
assert(a != b); // 1 != 0

a = 0;
b = --a; // a先自减再赋值给b
assert(a == b); // -1 == -1

a = 0;
b = a--; // a先赋值再自减
assert(a != b); // -1 != 0
复制代码

Equality and relational operators

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

Operator Meaning
== 相等运算符
!= 不等运算符
> 大于运算符
< 小于运算符
>= 大于等于运算符
<= 小于等于运算符

为了测试两个对象x,y是一样的东西,使用==运算符(在极少数的状况下,你须要知道两个对象是不是彻底相同的对象,这时使用identical()函数),如下是work的工做原理:

  1. 若是x或y不是空,若是两个都是null返回true,若是两个有一个不是null返回false
  2. 返回方法x.==(y)的结果(运算符如==是在第一个操做数上调用的方法,你甚至能够覆盖许多运算符,包括==,正如您在Overridable运算符中看到的那样。)

这里有一些使用相等运算符和关系运算符的例子:

assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);
复制代码

类型测试运算符Type test operators

asisis!运算符在运行时检查类型很方便。

Operator Meaning
as 类型转化 (也用于指定 库的前缀(library prefixes))
is 若是对象具备指定的类型,则为True
is! 若是对象具备指定的类型,则为False

obj is T是true,若是obj继承了由T指定的接口。如,obj is Object始终为true。

使用as运算符将对象强制转换为特定类型。一般,你应该使用as做为 is测试对象后跟使用对象表达式 的简写。例如,如下代码。

if (emp is Person) {
  // 检测类型
  emp.firstName = 'Bob';
}
复制代码

你可使用as运算符缩短代码

(emp as Person).firstName = 'Bob';
复制代码

注意:代码不是等效的。若是emp是非空的或者不是Person,第一个例子(有is)不作事,第二个例子(有as)抛出一个错误

赋值运算符Assignment operators

如你所见,你可使用=运算符赋值。要使用??=运算符,仅在是变量为null的状况下赋值。

// 值分配给a
a = value;
// 若是b为null,则将值赋给b;不然,b保持不变
b ??= value;
复制代码

复合赋值运算符+=,将操做与赋值组合在一块儿。

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

如下是复合赋值运算符的工做原理:

符合赋值 等价表达
对于运算符op a op= b a = a op b
例子: a += b a = a + b
var a = 2; // 赋值使用 =
a *= 3; // 赋值和乘法:a = a * 3 
assert(a == 6);
复制代码

逻辑运算符

可使用逻辑运算符进行运算(反转或者结合)布尔值。

Operator Meaning
!expr 反转如下表达式(将false改成true,反之亦然)
|| 逻辑或
&& 逻辑与

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

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

位操做与移位运算符Bitwise and shift operators

Operator Meaning
& 按位取与
| 按位取或
^ 按位异或
~expr 一元逐位补码(0s变为1s; 1s变为0s)
<< 左移
>> 右移

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

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); // 右移
复制代码

条件表达式Conditional expressions

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';
复制代码

前面的示例至少能够用另外两种方式编写,但没有这么简洁

// 稍微长一点的版本使用?:操做符。
String playerName(String name) => name != null ? name : 'Guest';

// 很长的版本使用if-else语句。
String playerName(String name) {
  if (name != null) {
    return name;
  } else {
    return 'Guest';
  }
}
复制代码

级联运算符Cascade notation (..)

级联操做符(..)容许同一个对象进行一系列操做。除了调用函数,你还能够访问统一对象上的字段。这节省你写临时变量的步骤,是你的代码更加优美。

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

首先调用方法,querySelector(),返回一个选择器对下个。接下来是级联运算符cascade notation operates在这个选择器对象上,忽略任何其后操做可能返回的值。

上面的例子等效于

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'.
复制代码

调用sb.write()返回void,你不能使用级联运算符对void

严格来说,级联”双点“不是一个运算符。它仅仅是dart语法的一部分。

其余运算符

在其余示例中,能够看到大多数剩余的运算符:

Operator Name Meaning
() 函数运用运算符 表示函数调用
[] 列表访问 引用列表中指定索引处的值
. 成员访问 指表达式的属性;:foo.bar从表达式foo中选择
?. 条件的成员访问 相似于.,但最左边的操做数能够为null;例如:foo?.bar从表达式foo中选择属bar,除非foonull(在这种状况下,foo?.bar的值为null)

有关.?...运算符的更多信息,查看类Classes

控制流语句

dart中控制流语句:

  • ifelse
  • for 循环
  • whiledo-while 循环
  • breakcontinue
  • switchcase
  • assert

你可使用 try-catchthrow,影响控制流语句如异常Exceptions所述。

If 和 else

dart支持if语句和else语句(可省略else语句),以下个例子所述。

if (isRaining()) {
  you.bringRainCoat();
} else if (isSnowing()) {
  you.wearJacket();
} else {
  car.putTopDown();
}
复制代码

dart语言不像JavaScript,控制流语句必须使用布尔值,详情Booleans

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());
复制代码
//改写JavaScript代码(非dart官方文档)
var callbacks = [];
for (var i = 0; i < 2; i++) {
  callbacks.push(() => console.log(i));
}
callbacks.forEach((c) => c());
复制代码

输出是01,在JavaScript中这个例子将打印两个2

若是你正在迭代的对象是迭代器,你可使用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 和 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();
}
复制代码

将上面的例子改写成迭代器Iterable(如列表或集合)

candidates
    .where((c) => c.yearsExperience >= 5)
    .forEach((c) => c.interview());
复制代码

Switch 和 case

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

注意:Dart中的Switch语句适用于有限的环境,例如interpreters(解释器)或scanners(扫描仪)。

每一个非空case子句以break语句结束。结束非空case子句的其余有效方法是continue,throw或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

  case 'CLOSED':
    executeClosed();
    break;
}
复制代码

然而,Dart确实支持空的case子句,容许某种形式的省略

var command = 'CLOSED';
switch (command) {
  case 'CLOSED': // 空case语句,运行下一个case
  case 'NOW_CLOSED':
    // 运行CLOSED和NOW_CLOSED。
    executeNowClosed();
    break;
}
复制代码

若是您真的想要跳转下一个case,可使用continue语句和标签:

var command = 'CLOSED';
switch (command) {
  case 'CLOSED':
    executeClosed();
    continue nowClosed;
  // 在nowClosed case上执行。

  nowClosed:
  case 'NOW_CLOSED':
    // 运行CLOSED和NOW_CLOSED。
    executeNowClosed();
    break;
}
复制代码

case子句能够具备局部变量,这些变量仅在该子句的范围内有效。

Assert

在程序开发期间,使用assert语句——assert(condition,optionalMessage);若是condition的结果为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'));
复制代码

也能够自定义错误的信息,添加一个字符串做为第二个参数。

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

assert()第一个参数能够是任意的表达式,若是表达式的结果为true,程序正常运行。反之,一个异常抛出。

在如下工具和框架的下,assert()的正常运行。

  • Flutter在debug 模式下
  • 开发者工具,例如:dartdevc默认支持。
  • 一些工具,如dartdart2js,经过命令行添加 --enable-asserts

在生产(正式运行)中,assert被忽略,assert不会评估的参数

异常

Dart代码会抛出和捕捉异常。异常是一种错误说明未知的错误发生。若是未捕获异常,异常会被抛出,致使抛出异常的代码终止执行。

与Java语言相反,Dart所有的意外都为非检查异常。methods不必定声明了它们可能抛出的异常,而且不要求捕获任何异常。

Dart提供了 ExceptionError,以及他们的子类。你能够定义本身的异常。可是,Dart程序能够抛出任何非null对象 - 不只仅是Exception和Error对象 - 做为异常。

Throw

如下是抛出或引起异常的示例:

throw FormatException('Expected at least 1 section');
复制代码

你也能够抛出任意对象:

throw 'Out of llamas!';
复制代码

注意:生产代码一般会抛出错误实现Error或异常Exception的类。

由于抛出异常是一个表达式,因此能够抛出异常在=>语句中以及容许表达式的任何其余地方:

void distanceTo(Point other) => throw UnimplementedError();
复制代码

Catch

捕捉异常,阻止异常继续传递(除非你从新抛出异常)。捕捉异常试你有机会处理它

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  buyMoreLlamas();
}
复制代码

要处理可能抛出不止一种异常类型的代码,能够指定多个catch子句。一个catch子句对应异常对象的类型以后处理异常。若是catch子句未指定类型,则该子句能够处理任何类型的抛出对象:

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // 一个特殊的异常
  buyMoreLlamas();
} on Exception catch (e) {
  // 还有其余任何异常
  print('Unknown exception: $e');
} catch (e) {
  // 处理全部没有指定的类型
  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++); // 返回一个错误
  } catch (e) {
    print('misbehave() partially handled ${e.runtimeType}.');
    rethrow; // 容许调用者看到异常。
  }
}

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

Final

要确保某些代码执行,不管是否有异常都须要执行。使用 finally语句。若是没有catch 语句匹配到异常,在final子句运行以后再抛出异常。

try {
  breedMoreLlamas();
} finally {
  // 老是清理,即便抛出异常。
  cleanLlamaStalls();
}
复制代码

finally 子句在catch子句以后运行:

try {
  breedMoreLlamas();
} catch (e) {
  print('Error: $e'); // 首先处理异常
} finally {
  cleanLlamaStalls(); // 以后清理
}
复制代码

想了解更多信息,阅读库预览的Exceptions模块。

classes

Dart语言是一个面向对象的语言,拥有类和基于mixin的继承。每一个类是类的一个实例,全部的类都来源与Object。基于mixin的继承意味着尽管每一个类(除了Object)有一个额外的超类,类主体能够在多个类层次结构中重用。

类成员

Object的成员由函数和数据(方法和实例变量)组成。当你调用一个方法,能够在对象上调用它:该方法能够访问该对象的函数和数据

使用点(.)来访问实例变量和方法:

var p = Point(2, 2);

// 设置实例变量y的值。
p.y = 3;

// 得到y的值。
assert(p.y == 3);

// 调用p上的 distanceTo() 
num distance = p.distanceTo(Point(4, 4));
复制代码

使用?.取代.避免因最左边的变量为空而引起的异常。

//若是p为非null,则将其y值设置为4。
p?.y = 4;
复制代码

使用构造体

你能够建立对象使用构造体。构造体的名字能够是类的名字(ClassName )或者类内的方法(ClassName.identifier)。例如,下面的代码建立Point对象使用Point()Point.fromJson()构造:

var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
复制代码

如下代码具备相同的效果,new为可选的关键字:

var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});
复制代码

版本说明:新关键字在Dart 2中变为可选。

一些方法提供常量构造器(constant constructors)。在构造函数以前放const建立编译时常量。

var p = const ImmutablePoint(2, 2);
复制代码

两个相同的编译时常量他们是相同的实例

var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

assert(identical(a, b)); // 他们是相同的实例
复制代码

在常量上下文中,能够在构造函数或字面量以前省略const。例如,该代码建立const的map:

// 这里由不少的const
const pointAndLine = const {
  'point': const [const ImmutablePoint(0, 0)],
  'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
复制代码

你能够省略除第一个外的const关键词

// Only one const, which establishes the constant context.
const pointAndLine = {
  'point': [ImmutablePoint(0, 0)],
  'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};
复制代码

若是常数构造函数在常数上下文以外没有const,它会建立一个**很是数(non-constant object)**对象:

var a = const ImmutablePoint(1, 1); // 建立常量
var b = ImmutablePoint(1, 1); // 不建立常量

assert(!identical(a, b)); // 不是同一个实例!
复制代码

版本说明:在Dart 2的常量上下文中,const关键字变为可选。

获取对象类型

使用ObjectruntimeType的属性能够在运行时返回type对象

print('The type of a is ${a.runtimeType}');
复制代码

至此,您已经了解了如何使用类。本节的其他部分将展现如何实现类。

实例变量

如下是声明实例变量的方法:

class Point {
  num x; // 声明实例变量x,最初为null。
  num y; // 声明y,最初为null。
  num z = 0; // 声明y,最初为null。
}
复制代码

全部为初始化的实例变量的初始值为null。

全部的实例变量隐式的定义了一个getter方法,非final的实例变量也隐式的生成了一个setter方法。详情Getters 和 setters

class Point {
  num x;
  num y;
}

void main() {
  var point = Point();
  point.x = 4; // 使用x的setter方法。
  assert(point.x == 4); // 使用x的getter方法。
  assert(point.y == null); // 值的默认值为null
}
复制代码

若是在变量的声明处初始化它(不是构造函数或者方法),这个值在建立实例的时候设置。建立实例的时间在构造函数和初始化列表执行以前。

class Point {
  num x, y;

  Point(num x, num y) {
    //有一个更好的方法来作到这一点,请继续关注。 
    this.x = x;
    this.y = y;
  }
}
复制代码

this关键字引用当前实例。

注意:仅在存在名称冲突时使用此选项。Dart省略了this

将构造函数参数建立实例变量的模式很是常见,dart语法使其加糖,很是简单:

class Point {
  num x, y;

  // 用于设定x和y的句法糖
  // 在构造函数体运行以前。
  Point(this.x, this.y);
}
复制代码

默认构造函数(Default constructors)

若是没有声明构造函数,则会有默认的构造函数。默认构造函数没有参数,在超类(被继承的类)中调用没有参数的的构造函数

构造函数不会继承(Constructors aren’t inherited)

子类不会继承超类的构造函数。子类没有定义构造函数,则会由一个只有默认(无参数,无名称)的构造函数

命名构造函数(Named constructors)

使用命名构造函数为一个类提供多种构造函数:

class Point {
  num x, y;

  Point(this.x, this.y);

  // 命名构造函数
  Point.origin() {
    x = 0;
    y = 0;
  }
}
复制代码

构造函数没有继承,这意味着超类的命名构造函数不会被子类继承。若是但愿使用超类中定义的命名构造函数建立子类,则必须在子类中实现超类的构造函数。

调用非默认的构造函数(Invoking a non-default superclass constructor)

默认,子类中的构造函数调用超类的未命名的无参数构造函数。超类的构造函数在构造函数体的运行以前被调用。若是使用初始化列表,则在调用超类以前执行。总之,执行顺序以下:

  1. 初始化列表
  2. 超类的无参数的构造函数
  3. 主类的无参数构造函数

若是超类不是未命名的无参数构造函数,则必须手动调用超类中的一个构造函数。超类构造函数在冒号(:)以后、构造函数体(若是存在)以前。

在下面的例子中,Employee类的构造函数为其超类Person调用命名构造函数。

class Person {
  String firstName;

  Person.fromJson(Map data) {
    print('in Person');
  }
}

class Employee extends Person {
  // Person does not have a default constructor;
  // you must call super.fromJson(data).
  Employee.fromJson(Map data) : super.fromJson(data) {
    print('in Employee');
  }
}

main() {
  var emp = new Employee.fromJson({});

  // Prints:
  // in Person
  // in Employee
  if (emp is Person) {
    // Type check
    emp.firstName = 'Bob';
  }
  (emp as Person).firstName = 'Bob';
}




//console
//in Person
//in Employee
复制代码

由于在调用构造函数以前会计算超类构造函数的参数,因此参数能够是一个表达式,例如函数调用:

class Employee extends Person {
  Employee() : super.fromJson(getDefaultData());
  // ···
}
复制代码

警告:超类构造函数的参数不能访问this。如,参数能够调用静态方法,但不能调用实例方法。

初始化列表(Initializer list)

除了调用超类构造函数以外,还能够在构造函数体运行以前初始化实例变量。用逗号分隔初始化程序。

// Initializer list sets instance variables before
// the constructor body runs.
// 初始化列表设置实例变量在构造体运行以前
Point.fromJson(Map<String, num> json)
    : x = json['x'],
      y = json['y'] {
  print('In Point.fromJson(): ($x, $y)');
}
复制代码

警告:初始化程序的右侧无权访问this

在开发期间,可使用初始化列表中的assert来验证输入。

Point.withAssert(this.x, this.y) : assert(x >= 0) {
  print('In Point.withAssert(): ($x, $y)');
}
复制代码

初始化列表赋值final字段时都驾轻就熟。 下面的示例初始化在初始化列表中三个final。

import 'dart:math';

class Point {
  final num x;
  final num y;
  final num distanceFromOrigin;

  Point(x, y)
      : x = x,
        y = y,
        distanceFromOrigin = sqrt(x * x + y * y);
}

main() {
  var p = new Point(2, 3);
  print(p.distanceFromOrigin);
}
//3.605551275463989
复制代码

重定向构造函数(Redirecting constructors)

有时构造函数的惟一目的是重定向到同一个类中的另外一个构造函数。重定向构造函数的主体是空的,构造函数调用出如今冒号(:)以后。

class Point {
  num x, y;

  // 该类的主构造函数。
  Point(this.x, this.y);

  // 委托主构造函数。
  Point.alongXAxis(num x) : this(x, 0);
}
复制代码

常量构造函数(Constant constructors)

若是生成从不不会更改的对象,则可使这些对象成为编译时常量。为此,请定义const构造函数并确保全部实例变量都是final

class ImmutablePoint {
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);

  final num x, y;

  const ImmutablePoint(this.x, this.y);
}
复制代码

常量构造函数并不老是建立常量。有关详细信息,请参阅关于实例变量(using constructors)的部分。

工厂构造函数(Factory constructors)

实现一个构造函数并不老是建立其类的新实例时,使用工厂的关键字。 例如,一个工厂构造可能从缓存返回一个实例,或者它可能会返回一个子类型的实例。

下面的例子演示了一个工厂构造函数从缓存中返回的对象:

class Logger {
  final String name;
  bool mute = false;

  // _cache是库私有的
  // 在它的名字前面_
  static final Map<String, Logger> _cache =
      <String, Logger>{};

  factory Logger(String name) {
    return _cache.putIfAbsent(
        name, () => Logger._internal(name));
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}
复制代码

注意:工厂构造函数无权访问this

像调用任何其余构造函数同样调用工厂构造函数:

var logger = Logger('UI');
logger.log('Button clicked');
复制代码

方法

方法是为对象提供行为的函数。

实例方法

在对象上的实例方法能够访问实例变量和thisdistanceTo()方法

下面的distanceTo()函数就是实例方法:

import 'dart:math';

class Point {
  num x, y;

  Point(this.x, this.y);

  num distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    return sqrt(dx * dx + dy * dy);
  }
}
复制代码

Getters 和 setters

Getters和Setters是对象属性的读写访问权限特殊的方法,每个实例变量有隐式的getter方法和若是合适的话还有setter方法。你能够经过 getters 和 setters建立额外的属性,使用getset关键词。

class Rectangle {
  num left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);

  // 定义两个计算属性:右侧和底部。
  num get right => left + width;
  set right(num value) => left = value - width;
  num get bottom => top + height;
  set bottom(num value) => top = value - height;
}

void main() {
  var rect = Rectangle(3, 4, 20, 15);
  assert(rect.left == 3);
  rect.right = 12;
  assert(rect.left == -8);
}
复制代码

借助于 getter 和 setter ,你能够直接使用实例变量,而且在不改变客户代码的状况下把他们包装成方法。

不管是否显示定义getter,相似自增(++)的操做符都已以预期方式工做。为了不产生任何意外的影响,操做符只要调用一次 getter ,就会把他的值存在临时变量里。

抽象方法

实例getter和setter能够是抽象的,定义接口,把实现的方法留给其余的类。抽象的方法仅存在于 抽象类(abstract classes)中。

用分号;代替方法体时方法变为抽象。

abstract class Doer {
  // 定义方法和变量

  void doSomething(); // 定义抽象方法
}

class EffectiveDoer extends Doer {
  void doSomething() {
    // 提供一个实现,这里的方法不是抽象的
  }
}
复制代码

抽象类

使用abstract修饰符来定义抽象类——类不能被实例化。抽象类对定义接口是有用的,一般还有一些实现。若是你想你的抽象的类是可实例化的,请定义为工厂构造函数factory constructor

抽象类一般有抽象方法。这是一个声明具备抽象方法的抽象类的示例:

// 这个类被声明为抽象的
// 没法被实例化
abstract class AbstractContainer {
  // 定义构造函数,字段,方法......

  void updateChildren(); // 抽象方法
}
复制代码

隐式接口

每一个类都隐式定义了一个接口,每一个类包含类的实例和他的实现的全部接口的实例。若是你想建立一个类A支持B的API,不继承B的实现,class A 应该implement B的接口。

类经过implement实现一个或者更多的接口,而后提供接口须要的API。

// Person的类,实现了接口greet()
class Person {
  // 在接口中,可是它仅仅对于这个库可见
  final _name;

  // 不在接口中,由于是一个构造函数
  Person(this._name);

  // 在接口中
  String greet(String who) => 'Hello, $who. I am $_name.';
}

// Person接口的实现
class Impostor implements Person {
  get _name => '';

  String greet(String who) => 'Hi $who. Do you know who I am?';
}

String greetBob(Person person) => person.greet('Bob');

void main() {
  print(greetBob(Person('Kathy')));
  print(greetBob(Impostor()));
}
复制代码

一个类实现多接口的实例:

class Point implements Comparable, Location {...}
复制代码

继承类

使用extends建立子类,使用super来引用父级

class Television {
  void turnOn() {
    _illuminateDisplay();
    _activateIrSensor();
  }
  // ···
}

class SmartTelevision extends Television {
  void turnOn() {
    super.turnOn();
    _bootNetworkInterface();
    _initializeMemory();
    _upgradeApps();
  }
  // ···
}
复制代码

重写成员

子类能够重写实例成员,方法、getters和setters。你可使用@override注解代表你重写了一个成员

class SmartTelevision extends Television {
  @override
  void turnOn() {...}
  // ···
}
复制代码

使用协变关键字(covariant keyword) 在类型安全的代码中缩小方法中参数的类型或者缩小实例变量的类型。

覆写运算符

能够复写如下表格的运算符,例如,若是定义Vector类,则能够定义一个+方法来添加两个向量。

< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
% >>

注意:您可能已经注意到!=不是可覆写的运算符。表达式e1!= e2只是!(e1 == e2)的语法糖。

覆写+和 - 运算符的类的示例:

class Vector {
  final int x, y;

  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

  // 运算符==和hashCode未显示。有关详情,请参阅下面的注释。
  // ···
}

void main() {
  final v = Vector(2, 3);
  final w = Vector(2, 2);

  assert(v + w == Vector(4, 5));
  assert(v - w == Vector(0, 1));
}
复制代码

若是你覆写==,你应该也覆写hashCode的对象。例如重写==hashCode 实现map键(Implementing map keys)。获取更多的信息,一般,看扩展类(Extending a class)

noSuchMethod()

要在使用不存在的实例和方法时作出检测或者作出反应,能够覆盖noSuchMethod()

class A {
  // 除非你覆写 noSuchMethod
  // 不存在的成员致使NoSuchMethodError。
  @override
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: ' +
        '${invocation.memberName}');
  }
}
复制代码

重写noSuchMethod以后,知足如下条件才能调用未写的方法:

  • 接收者有dynamic类型
  • 接收者的类型有一个定义了未实现的方法(能够是抽象方法)和重写noSuchMethod的方法。

如下是我查阅资料对官方文档的补充

// 会报错
class Person {
    @override
     noSuchMethod(Invocation msg) => "got ${msg.memberName} "
      "with arguments ${msg.positionalArguments}";
} 
void main() {
    var person = new Person();
    print(person.missing("20", "Shubham")); 
 }
复制代码
class Person {
  missing(int age, String name); // 抽象方法
  @override
  noSuchMethod(Invocation msg) => "got ${msg.memberName} "
      "with arguments ${msg.positionalArguments}";
}

main(List<String> args) {
  dynamic person = new Person(); //person可使用var、Person 或者 dynamic
  print(person.missing(20, 'shubham')); // 调用抽象方法
}

复制代码
class Person {
  missing(int age,String name);  // 抽象方法

  @override // 重写noSuchMethod
    noSuchMethod(Invocation invocation) => 'Got the ${invocation.memberName} with arguments ${invocation.positionalArguments}';
}

main(List<String> args) {
  dynamic person = new Person(); //person可使用var、Person 或者 dynamic
  print(person.missing(20,'shubham')); // 调用抽象方法
}
复制代码

获取更多信息,查看 noSuchMethodui运转说明(noSuchMethod forwarding specification)

枚举类型

枚举类型是一种用于表示固定数量的常量值的一种特殊类的特殊类。

Using enums

使用enum关键字声明枚举类型:

enum Color { red, green, blue }
复制代码

每个枚举有一个indexgetter,调用它能够返回一个位置的值(0为基准)。例如第一个index是0,第二个值是1.

assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
复制代码

使用枚举的values ,能够获得列表化的枚举。

List<Color> colors = Color.values;
assert(colors[2] == Color.blue);
复制代码

能够在 switch语句(switch statements)中使用枚举,若是你不处理全部的枚举值,你会获得一个警告。

var aColor = Color.blue;

switch (aColor) {
  case Color.red:
    print('Red as roses!');
    break;
  case Color.green:
    print('Green as grass!');
    break;
  default: // 没有这个你将会获得一个警告
    print(aColor); // 'Color.blue'
}
复制代码

枚举类型有如下限制:

  • 你不能子类化,混合或实现枚举。
  • 你没法显式实例化枚举。

有关更多信息,请参阅Dart语言规范(Dart language specification)

为类添加新功能:mixins

混入(Mixins)是一种多个类层次中重用代码的方法。

使用with关键字后跟一个或多个mixin名来使用混入。接下来这两个类显示如何使用混入:

class Musician extends Performer with Musical {
  // ···
}

class Maestro extends Person with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}
复制代码

建立一个继承Object的类和不声明构造函数来实现mixin,除非您但愿mixin可用做常规类,不然使用mixin关键字而不是class。例如:

mixin Musical {
  bool canPlayPiano = false;
  bool canCompose = false;
  bool canConduct = false;

  void entertainMe() {
    if (canPlayPiano) {
      print('Playing piano');
    } else if (canConduct) {
      print('Waving hands');
    } else {
      print('Humming to self');
    }
  }
}
复制代码

使用on指定某一种类型(超类是指定的类型)能够用mixin,这样你的mixin能够调用它未定义的方法。

mixin MusicalPerformer on Musician {
  // ···
}
复制代码

版本差别:Dart2.1引入了关键词mixin,早期版本中的代码一般使用抽象类。有关2.1 mixin更改的更多信息,请参阅Dart SDK 版本日志(Dart SDK changelog)2.1 mixin规范(2.1 mixin specification)

类变量和方法

使用static关键字实现类范围的变量和方法。

静态变量

静态变量(类变量)是有用的对于类范围的状态和常量

class Queue {
  static const initialCapacity = 16;
  // ···
}

void main() {
  assert(Queue.initialCapacity == 16);
}
复制代码

静态变量不能被初始化直到他们被使用。

注意:此页面以下更喜欢lowerCamelCase(驼峰命名)为常量名的风格指南建议(style guide recommendation)

静态方法

静态方法(类方法)不对实例进行操做,所以没法访问它。例如:

import 'dart:math';

class Point {
  num x, y;
  Point(this.x, this.y);

  static num distanceBetween(Point a, Point b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
  }
}

void main() {
  var a = Point(2, 2);
  var b = Point(4, 4);
  var distance = Point.distanceBetween(a, b);
  assert(2.8 < distance && distance < 2.9);
  print(distance);
}
复制代码

注意:对于经常使用或普遍使用的实用程序和功能,考虑使用顶级函数而不是静态方法。

可使用静态方法做为编译时常量。能够将静态方法做为参数传递给常量构造函数。

范类

若是你看了基础类型数组List的API文档,你将会看到List<E>。这个尖括号里面标记的将List制成通用的参数化的类型——具备正式类型参数的类型。按照惯例,大多数类型变量都有单字母名称,例如E,T,S,K和V.

为何使用范类?

范类常常用做安全的类型时必须的,可是他们更有好处:

  • 正确指定泛型类型能够生成更好的代码。
  • 可使用泛型来减小代码重复。

若是你打算让列表只包含字符串,你能够声明为的List 的(读取为“字符串列表”)。接下来的代码中,编译器能够检测到指定的添加非字符串到列表这种方式是一个错误。

//错误
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // Error
复制代码

使用范类的另外一个缘由是减小代码。范类可让你有许多类型间有统的个接口和实现,同时仍然利用静态分析。例如,建立一个接口对caching对象

abstract class ObjectCache {
  Object getByKey(String key);
  void setByKey(String key, Object value);
}
复制代码

你发现你想要一个指定字符串版本的接口,你能够建立另一个接口

abstract class StringCache {
  String getByKey(String key);
  void setByKey(String key, String value);
}
复制代码

以后你发现你先要一个数字的接口,你有了这个想法

范类能够解决你的问题建立接口,你能够建立一个带有类型参数的接口。

abstract class Cache<T> {
  T getByKey(String key);
  void setByKey(String key, T value);
}
复制代码

在此代码中,T是替身类型。它是一个占位符,能够将其视为开发人员稍后定义的类型。

使用集合字面量

List,set和map能够被参数化。参数化的字面量就像您已经看到的字面量同样,除了添加<type> <keyType, valueType>(对于map)在括号以前。这里是一些使用字面量类型的例子

var names = <String>['Seth', 'Kathy', 'Lars'];
var uniqueNames = <String>{'Seth', 'Kathy', 'Lars'};
var pages = <String, String>{
  'index.html': 'Homepage',
  'robots.txt': 'Hints for web robots',
  'humans.txt': 'We are people, not machines'
};
复制代码

使用带有构造函数的参数化类型

当使用构造函数时指定一种或更多种的类型,放在尖括号内<...>就在类名以后

var nameSet = Set<String>.from(names);
复制代码

如下代码建立一个具备整数键和View类型值的映射:

var views = Map<int, View>();
复制代码

通用集合及其包含的类型

Dart通用的类型时具体化的,这意味着它们在运行时携带其类型信息。例如,您能够测试集合的类型:

var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
复制代码

注意:与Dart相反,Java中的泛型使用擦除,这意味着在运行时删除泛型类型参数。在Java中,您能够测试对象是否为List,但没法测试它是否为List <String>

限制参数化类型

当实现一个范类,你可能想去限制它的参数的类型。你可使用extends

class Foo<T extends SomeBaseClass> {
  // 实如今这儿
  String toString() => "Instance of 'Foo<$T>'";
}

class Extender extends SomeBaseClass {...}
复制代码

能够将SomeBaseClass或其任何子类用做通用参数:

var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();// Extender extends SomeBaseClass
复制代码

也能够不指定任何通用参数:

var foo = Foo();
print(foo); // Instance of 'Foo<SomeBaseClass>'
复制代码

指定任何非SomeBaseClass类型都会致使错误:

var foo = Foo<Object>();
复制代码

使用范类方法

最初,Dart的范类支持仅限于类。范类方法是较新的语法,容许在方法和函数上使用类型参数:

T first<T>(List<T> ts) {
  // 作一些初步的工做或错误检查,而后...
  T tmp = ts[0];
  // 作一些额外的检查或处理...
  return tmp;
}
复制代码

这里是范类参数,first (<T>) 容许在多个地方使用类型参数T

  • 函数返回值为(T
  • 参数类型中(List<T>
  • 局部变量中(T tmp

查看使用范类方法(Using Generic Methods)获取更多的信息

库和可见性

importlibrary指令能够帮助你建立一个模块化的共享的代码。库不只仅提供API,但也有私有元素:如下划线(_)开头的标识符仅在库中可见。每一个Dart应用程序都是一个库,即便它不使用库指令。

库能够分布式的使用包 packages

使用库

使用import指定在一个库命名空间来引用这个库。

例如,Dart Web应用程序一般使用dart:html库,它们能够像这样导入:

import 'dart:html';
复制代码

导入所需的唯一参数是指定库的URI。对于内置库,URI具备特殊的dart: scheme。对于其余库,可使用文件系统路径或package:schemepackage:scheme指定包管理器(如pub工具)提供的库。例如

import 'package:test/test.dart';
复制代码

注意:URI表明统一资源标识符。 URL(统一资源定位符)是一种常见的URI。

指定库前缀

若是导入两个标识符冲突的库,则能够为一个或两个库指定一个前缀。例如,若是library1library2都具备Element类,那么可能具备如下代码:

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

// 使用 Element 来自于lib1
Element element1 = Element();

// 使用 Element 来自于 lib2.
lib2.Element element2 = lib2.Element();
复制代码

导入库的一部分

若是只想使用一部分库,则能够有选择地导入该库。例如:

// 导入仅仅foo函数
import 'package:lib1/lib1.dart' show foo;

// 导入除foo之外iade函数
import 'package:lib2/lib2.dart' hide foo;
复制代码

延迟加载库

延迟加载(也称为延迟加载)容许Web应用程序在须要库时按需加载库。如下是可能使用延迟加载的一些状况:

  • 减小web app的初始化启动时间
  • 执行A / B测试-尝试的算法的替代实施方式中;
  • 加载不多使用的功能,例如可选的屏幕和对话框。

只有dart2js支持延迟加载。 Flutter,Dart VM和dartdevc不支持延迟加载。有关更多信息,请参阅issue #33118issue #27776

要延迟加载库,必须首先使用deferred as导入它。

import 'package:greetings/hello.dart' deferred as hello;
复制代码

当您须要库时,使用库的标识符调用loadLibrary()

Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}
复制代码

在上面的代码中,await关键字暂停执行,直到加载库。有关async和await的更多信息,请参阅异步支持(asynchrony support)

能够在库上屡次调用loadLibrary()而不会出现问题。该库只加载一次。

使用延迟加载时,请注意如下几点:

  • 延迟库的常量在其做为导入文件时不是常量。记住,这些常量不存在,直到迟库被加载完成。
  • 不能在导入文件中使用延迟库常量的类型。相反,考虑将接口类型移到同时由延迟库和导入文件导入的库。
  • Dart隐含调用LoadLibrary()插入到定义deferred as namespace。在调用LoadLibrary()函数返回一个Future。

实现库

查看Create Library Packages,得到如何实现一个库的包的建议,包括:

  • 如何组织库源代码。
  • 如何使用export 指令。
  • 什么时候使用part指令。
  • 什么时候使用library 指令。

异步支持

Dart库有不少返回Future或Stream对象的函数。这些函数是异步的。它们在设置可能耗时的操做(例如I / O)以后返回,而无需等待该操做完成。

asyncawait关键字支持异步编程,使能够编写看起来相似于同步代码的异步代码。

处理Futures

当你使用Futures的结果时,你有两个选项:

使用asyncawait的代码是异步的,可是看起来很像同步代码。例如,下面是一些使用await等待异步函数结果的代码:

await lookUpVersion();
复制代码

要使用await,代码必须位于async函数中,该函数被标记为async

Future checkVersion() async {
  var version = await lookUpVersion();
  // 作点什么
}
复制代码

注意:虽然一个async函数可能会执行耗时的操做,程序不会等待这些操做。 相反,async函数执行一直到它遇到其第一 await表达式(详细信息)。 而后它返回一个Future对象,恢复执行所述await表达完成以后。

使用try,catch,最后使用await处理代码中的错误和清除:

try {
  version = await lookUpVersion();
} catch (e) {
  // 对没法查找版本作出反应
}
复制代码

能够在async函数中屡次使用await。例如,如下代码等待三遍函数结果:

var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
复制代码

await expression中,这个表达式的值一般是一个Futrue;若是他不是,而后该值将自动包装在Future中。此Future对象指示返回一个对象的承诺。await expression的值是返回的对象。await expression使执行暂停,直到该对象可用为止。

**若是在使用await时收到编译时错误,请确保await在异步函数中。**例如使用await在你的app的main()函数,这个main()的函数体必须标记成async

Future main() async {
  checkVersion();
  print('In main: version is ${await lookUpVersion()}');
}
复制代码

声明异步函数

异步函数是一个函数,其主体带有async的修饰符

向函数添加async关键字使其返回Future。例如,考虑如下同步函数,该函数返回一个String:

String lookUpVersion() => '1.0.0';
复制代码

若是将其更改成async函数(例如,因为未来的实现会很耗时),则返回的值将为Future:

Future<String> lookUpVersion() async => '1.0.0';
复制代码

请注意该函数的体并不须要使用Future 的API。 Dart若是有必要创造Future的对象。 若是你的函数不返回一个有用的值,使得它的返回类型 Future<void>

对于交互式介绍如何使用Future,asyncawait,看异步编程代码实验室(asynchronous programming codelab).

处理流

从流内获得一个值,你有两个选项

注意:在使用wait for以前,请确保它使代码更易读,而且确实须要等待全部流的结果。例如,一般不该该为UI事件侦听器使用wait For,由于UI框架会发送无穷无尽的事件流。

异步for循环具备如下形式

await for (varOrType identifier in expression) {
  // 每次流发出一个值时执行。
}
复制代码

expression 表达式的值必须有 类型流,执行过程以下:

  1. 等待流发出一个值
  2. 执行循环体,包含流发出的值
  3. 循环1和2直到流关闭

要中止收听流,可使用break或return语句,该语句会脱离for循环并注销流。

若是在实现异步for循环时收到编译时错误,请确保await for位于异步函数中。例如使用await在你的app的main()函数,这个main()的函数体必须标记成async

Future main() async {
  // ...
  await for (var request in requestServer) {
    handleRequest(request);
  }
  // ...
}
复制代码

有关异步编程的更多信息,请参见库介绍的 dart:async 部分。

生成器

当须要延迟生成值序列时,请考虑使用生成器函数。 Dart具备对两种生成器功能的内置支持:

  • Synchronous 生成器:返回一个 Iterable对象
  • Asynchronous 生成器:返回一个 Stream对象

标记函数体为sync*实现一个synchronous 的生成器函数,并使用yield语句传递值:

Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}
复制代码

标记函数主体标记为async*,实现asynchronous 生成器函数,并使用yield语句传递值:

Stream<int> asynchronousNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}
复制代码

若是生成器是递归的,则可使用yield*来提升其性能:

Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}
复制代码

可调用类

要容许像函数同样调用Dart类的实例,实现call()方法。在下面的示例中,WannabeFunction类定义了一个call()函数,该函数接受三个字符串并将它们链接起来,每一个字符串之间用空格隔开,并附加一个感叹号。单击Run执行代码。

class WannabeFunction {
  call(String a, String b, String c) => '$a $b $c!';
}
main() {
  var wf = new WannabeFunction();
  var out = wf("Hi","there,","gang");
  print('$out');
}

// Hi there, gang!
复制代码

Isolates

大多数计算机,即便在移动平台上,也有多核CPU。 为了利用全部这些核心,开发人员传统上使用并发运行的共享内存线程。 可是,共享状态并发容易出错,而且可能致使代码复杂化。

全部Dart代码都在隔离区内运行,而不是线程。 每一个隔离区都有本身的内存堆,确保不会从任何其余隔离区访问隔离区的状态。

有关更多信息,请参阅如下内容:

Typedefs

在dart中,函数是对象,就像字符串和数字是对象同样。typedef或函数类型别名为函数类型提供了一个名称,能够在声明字段和返回类型时使用该名称。当函数类型被分配给变量时,typedef保留类型信息。

请考虑如下不使用typedef的代码:

class SortedCollection {
  Function compare;

  SortedCollection(int f(Object a, Object b)) {
    compare = f;
  }
}

// 初始化, 不执行。
int sort(Object a, Object b) => 0;

void main() {
  SortedCollection coll = SortedCollection(sort);

  // 咱们都知道compare 是一个函数,
  // 可是什么类型的函数呢?
  assert(coll.compare is Function); 
}
复制代码

分配f进行compare时,类型信息会丢失。f的类型是 (Object, Object)int(→ 意味着返回的),然而compare 的类型是Function 。若是咱们使用显式的名字更改代码并保留类型信息,则开发者和工具均可以使用这些信息。

typedef Compare = int Function(Object a, Object b);

class SortedCollection {
  Compare compare;

  SortedCollection(this.compare);
}

// 初始化,不执行。
int sort(Object a, Object b) => 0;

void main() {
  SortedCollection coll = SortedCollection(sort);
  assert(coll.compare is Function);
  assert(coll.compare is Compare);
}
复制代码

注意: 目前 typedefs 仅限于函数类型,咱们指望这一点能有所改变。

由于 typedefs 是简单的别名,因此它提供了一种方法来检查任何函数的类型。好比:

typedef Compare<T> = int Function(T a, T b);

int sort(int a, int b) => a - b;

void main() {
  assert(sort is Compare<int>); // True!
}
复制代码

元数据

使用元数据在你的代码中添加一个附加的信息,元数据声明开始一个@,接着式对编译时常量的引用(例如deprecated)或对常量构造函数的调用。

全部Dart代码均可以使用两个注释:@deprecated@override。有关使用@override的示例,请参见 扩展类Extending a class。这是使用@deprecated注释的示例:

class Television {
  /// _Deprecated: 使用 [turnOn] 代替._
  @deprecated     // 标记为不推荐,直到下一个版本
  void activate() {
    turnOn();
  }

  /// Turns the TV's power on.
  void turnOn() {...}
}
复制代码

您能够定义本身的元数据注释。这是定义带有两个参数的@todo注释的示例:

library todo;

class Todo {
  final String who;
  final String what;

  const Todo(this.who, this.what);
}
复制代码

这是使用@todo批注的示例:

import 'todo.dart';

@Todo('seth', 'make this do something')
void doSomething() {
  print('do something');
}
复制代码

元数据能够出如今库,类,typedef,类型参数,构造函数,工厂,函数,字段,参数或变量声明以前,也能够出如今导入或导出指令以前。您能够在运行时使用反射来检索元数据。

注释

Dart支持单行注释,多行注释和文档注释。

单行注释

单行注释以//开头。 Dart编译器会忽略//和行尾之间的全部内容。

void main() {
  // TODO: refactor into an AbstractLlamaGreetingFactory?
  print('Welcome to my Llama farm!');
}
复制代码

多行注释

多行注释以 /*开头,以*/结尾。 Dart编译器将忽略/**/之间的全部内容(除非注释为文档注释;请参阅下一节)。多行注释能够嵌套。

void main() {
  /* * This is a lot of work. Consider raising chickens. Llama larry = Llama(); larry.feed(); larry.exercise(); larry.clean(); */
}
复制代码

文档注释

文档注释是与 ////**开始多行或单行注释。 连续的行上使用 ///有一个多行文档注释一样的效果。

内部文档注释,Dart编译器忽略,除非它放在括号中的全部文本。 使用括号,能够参考类,方法,字段,顶级的变量,函数和参数。括号中的名称在文档程序元素的词法范围内解析

下面是与其余类和论点引用文档注释的例子:

/// A domesticated South American camelid (Lama glama).
///
/// Andean cultures have used llamas as meat and pack
/// animals since pre-Hispanic times.
class Llama {
  String name;

  /// Feeds your llama [Food].
  ///
  /// The typical llama eats one bale of hay per week.
  void feed(Food food) {
    // ...
  }

  /// Exercises your llama with an [activity] for
  /// [timeLimit] minutes.
  void exercise(Activity activity, int timeLimit) {
    // ...
  }
}
复制代码

在生成的文档中, [food] 变成了指向 Food 类的 API 文档链接。

为了转换 Dart 代码并生成 HTML 文档,你可使用 SDK 的 文档生成器。生成文档的示例,请参阅 Dart API 文档。关于如何组织你的文档,请参阅 文档注释准则

总结

此页面总结了Dart语言的经常使用功能。 更多功能正在实施,但咱们预计,他们将不会破坏现有的代码。 欲了解更多信息,请参见 Dart language specificationEffective Dart.。

要了解更多关于Dart的核心库,看A Tour of the Dart Libraries.

参考连接

dart.dev/guides/lang…

stackoverflow.com/questions/5…

相关文章
相关标签/搜索