Dart 语言学习笔记

基础语法

注释

Dart 的单行注释也是 // ,注释内容最好是个完整的句子,一般以大写字母开头,以句点或其余标点符号结束:html

// Not if there is nothing before it.
if (_chunks.isEmpty) return false;
复制代码

/* ... */ 只适合用来注掉某一段暂时不用的代码,不适合当成注释使用。
/// 是 Dart 推荐使用的文档注释语法,dartdoc 会根据文档注释生成 HTML 文档,使用方括号能够连接到一个类、方法、字段等位置:git

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

因为历史缘由, Dart 也支持 JavaDoc 样式的文档注释 /** ... */,但这种类型的文档注释会产生两个无实际内容的行,对于使用 * 标志列表项等场景并不方便,因此推荐使用 /// 文档注释。github

变量

能够赋值给变量的都是对象,包括数字、函数和 null 对象,对象都是类的实例,都继承自 Object
变量存的是引用。
虽然 Dart 是强类型语言,可是类型声明是可选的,由于 Dart 能够推断出变量到底是什么类型的。
可使用 var 关键字声明变量而无需指定具体类型,如 var name = 'Bob';var number = 42;
有些操做须要处理任何可能的对象,如 log() 方法须要调用给定对象的 toString()。而在 Dart 中有两种类型: ObjectdynamicObject 适合表示能够是任何对象的场景,而 dynamic 适合表示更复杂的类型,如意味着 Dart 的类型系统已经不足以表示的一系列容许的类型,或者值来自 interop 或 其余超过静态类型系统范围的类型,或者你想明确地声明运行时动态处理的变量:express

void log(Object object) {
  print(object.toString());
}

/// Returns a Boolean representation for [arg], which must
/// be a String or bool.
bool convertToBool(dynamic arg) {
  if (arg is bool) return arg;
  if (arg is String) return arg == 'true';
  throw new ArgumentError('Cannot convert $arg to a bool.');
}
复制代码

final 和 const

若是变量永远不会改变,可使用 finalconst 去声明,final 变量只能赋值一次,const 变量是编译时常量(也是隐式 final 的),final 的顶级类变量是在其第一次被使用时初始化的。实例变量能够是 final 的,但不能是 const 的。
const 不单能声明常量,还能够建立常量值,或者声明能够建立常量值的构造器。编程

内置的类型

num 类型有两个子类型 intdoubleint 型位数不超过64位,在 Dart VM 上是 -263 到 263 - 1,double 是64位浮点数,遵循 IEEE 754 标准。
String 类型是 UTF-16 编码,可使用单引号也可使用双引号,所以能够灵活的避免字符串限定符。能够利用 ${expression} 在字符串中使用表达式的值,若是表达式是个标识符,那么 {} 能够省略。字符串拼接可使用紧邻的字符串或者 + 号:api

var s1 = 'String '
    'concatenation'
    " works even over line breaks.";
var s2 = 'The + operator ' + 'works, as well.';
复制代码

多行的字符串可使用三个单引号或三个双引号包裹。 可使用前缀 r 建立原始字符串:缓存

// 在原始字符串中,即便是 \n 也再也不是特殊字符了
var s = r"In a raw string, even \n isn't special.";
复制代码

Dart 的集合 API 包含 lists, sets, 和 maps。List 可使用传统构造器建立,也可使用 [] 语法建立,Map 可使用传统构造器建立,也可使用 {} 语法建立:bash

// Use a List constructor.
var vegetables = new List();
// Or simply use a list literal.
var fruits = ['apples', 'oranges'];
// Maps can be built from a constructor.
var searchTerms = new Map();
// Maps often use strings as keys.
var hawaiianBeaches = {
  'Oahu': ['Waikiki', 'Kailua', 'Waimanalo'],
  'Big Island': ['Wailea Bay', 'Pololu Beach'],
  'Kauai': ['Hanalei', 'Poipu']
};
复制代码

ListSet 都实现了 Iterable,能够经过迭代器去遍历,而 Map 能够经过它的keysvalues 属性获取迭代器。markdown

函数

Dart 是真正的面向对象语言,因此即便是函数也是对象,类型是 Function。函数做为一类对象(first-class objects),能够做为参数传递给其余函数。
若是函数体只包含一个表达式语句,可使用 => 简写,如:闭包

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

能够简写成:

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

这种箭头一般称做胖箭头(fat arrow)。
区分 expressionstatement 有时仍是挺重要的,不包含 if 等控制流语句的程序元素均可以称之为表达式,包括操做符表达式,return 子句,throw 子句,print 等函数的调用。

可选参数

可选参数有两种,命名可选参数和有序可选参数,可选参数必须放在全部必须参数的后面。命名可选参数的列表使用 {param1, param2, …} 声明,使用 paramName: value 调用。有序可选参数的列表使用 [param1, param2, …] 声明:

void enableFlags(String styleName, {bool bold, bool hidden}) {
}
...
enableFlags("NORMAL", hidden: false);
复制代码
String say(String from, String msg, [String device]) {
}
...
say('Bob', 'Howdy');
say('Bob', 'Howdy', 'smoke signal');
}
复制代码

可选参数能够经过 = 指定默认参数值,这个默认参数值必须是编译时常量,若是不指定默认参数值,那么默认参数值为 null,因此不要显示地设置默认值为 null:

void enableFlags({bool bold = false, bool hidden = false}) {
}
...
enableFlags(bold: true);
复制代码

main() 函数

每一个 app 都必须有一个顶层 main() 函数做为 app 的入口, main() 函数的参数列表是可选的 List<String> ,返回值是 void

匿名函数

没有名字的函数有时会称为 匿名函数(anonymous function)lambda 表达式(lambda)闭包(closure),匿名函数的声明与普通函数相似:

([[Type] param1[, …]]) { 
  codeBlock; 
}; 
复制代码

如:

var functionList = [];
  functionList.add((int element) {
    print("element:" + element.toString());
  });
复制代码

做用域

Dart 是一个 lexically scoped 语言,即 词法做用域/静态做用域 语言。也就是说,变量的做用域是根据它所处代码的布局位置静态决定的,你能够根据大括号去判断变量的做用域。

词法闭包

闭包(closure)就是一个函数对象能够访问它词法做用域内的变量,即便这个函数在原始范围外被使用。例如,下面的例子,makeAdder() 会捕获 addBy. 不管他返回的函数在哪,他都会记住 addBy:

/// 返回一个匿名函数,匿名函数的返回值是
/// 匿名函数的参数加上 [addBy].
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);
}
复制代码

返回值

全部函数都有返回值,若是不指定返回值,默认会隐式添加 return null; 到函数体中。

操做符

5 / 2 的值是浮点数 2.5,而5 ~/ 2 的值才是整型的 2。
判断对象类型用 isis! 操做符,使用 as 操做符进行类型转换:

if (emp is Person) {
  emp.firstName = 'Bob';
}
// 能够简写成下面的语句,可是若是emp 不是 Person 类型的
// 那么下面的语句会抛出异常
(emp as Person).firstName = 'Bob';
复制代码

使用 ??= 操做符能够给一个值为 null 的变量赋值。使用 ?. 能够避免左边表达式的值为空致使的异常,如 foo?.barfoo 不为空时返回 bar 属性,在 foo 为空时返回 null

级联符号(..)

级联符号 .. 可让你连续操做相同的对象,不单能够连续地调用函数,还能够连续地访问方法,这样作能够避免建立临时变量,从而写出更流畅的代码,流式编程更符合现代编程习惯和编程风格:

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

严格意义上说,级联符号不是操做符,而是 Dart 语法的一部分。

循环

若是循环(迭代)的对象是 Iterable 对象,能够利用它的 forEach() 方法对集合中的元素进行操做,forEach() 方法的参数是一个函数,它会按序应用集合中的每一个元素给这个函数:

void printElement(int element) {
  print(element);
}
var list = [1, 2, 3];
list.forEach(printElement);
复制代码

ListSet 这样的 Iterable 类还支持 for-in 循环:

var collection = [0, 1, 2];
for (var x in collection) {
  print(x); // 0 1 2
}
复制代码

异常处理

抛出的异常能够是任意对象,但最好是 ErrorException 对象:

throw new FormatException('Expected at least 1 section');
...
throw 'Out of llamas!';
复制代码

捕获异常可使用 oncatch 或者同时使用:

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // 捕获 OutOfLlamasException 类型的异常,不须要使用异常对象
  buyMoreLlamas();
} on Exception catch (e) {
  // 捕获 Exception 异常,打印异常对象
  print('Unknown exception: $e');
} catch (e) {
  // 捕获并处理任何类型的 thrown 对象
  print('Something really unknown: $e');
}
复制代码

catch() 能够有两个参数,第一个是抛出的异常对象,第二个是堆栈跟踪对象。若是捕获异常并处理后还容许该异常继续传播,可使用 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 子句会在 catch 子句执行完执行。若是没有 catch 子句,那么会在finally 子句运行完才开始传播异常。

类与继承

Dart 的继承是基于 mixin 的继承,也就是说,虽然每一个类(除了 Object 类)有且只有一个超类,但类体仍然能够在多个类层级上重用。
构造器的名字能够是 ClassName(无名构造器),也能够是 ClassName.identifier(命名构造器),实例化时的 new 关键字能够省略。
全部的实例变量都会自动生成隐式的 getter 方法,非 final 的实例变量也会自动生成隐式的 setter 方法,使用 getset 关键词能够建立额外的属性和其 getter/setter 方法的实现。
Dart 有个语法糖能够方便地将构造器参数赋值给实例变量(在构造器体执行以前):

class Point {
  num x, y;
  Point(this.x, this.y);
}
复制代码

若是不声明构造器,会隐式包含一个无名无参构造器,并调用父类的无名无参构造器。
子类不能继承父类的构造器,因此,若是若是子类想利用父类的命名构造器建立实例,就必须实现父类的命名构造器。若是父类没有无名无参构造器,那么你就必须手动调用父类的一个构造器:

class Person {
  String firstName;
  Person.fromJson(Map data) {
    print('in Person');
  }
}
class Employee extends Person {
  Employee.fromJson(Map data) : super.fromJson(data) {
    print('in Employee');
  }
}
复制代码

初始化的顺序为: 初始化列表 → 父类无参构造器 → 主类无参构造器
能够在构造器函数体执行前初始化实例变量,初始化列表使用逗号隔开,初始化列表特别适合初始化 final 字段:

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

  Point(x, y)
      : x = x,
        y = y,
        distanceFromOrigin = sqrt(x * x + y * y);
}
复制代码

重定向到同类其余构造器:

class Point {
  num x, y;

  // The main constructor for this class.
  Point(this.x, this.y);

  // Delegates to the main constructor.
  Point.alongXAxis(num x) : this(x, 0);
}
复制代码

若是类的对象永远不会改变,就可让这个对象成为编译时常量,只须要定义 const 构造器并确保全部的实例变量是 final 的:

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

  final num x, y;

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

可使用 factory 关键词声明工厂构造器,工厂构造器不必定要建立一个新的实例,能够直接返回缓存的实例,也能够返回子类型的实例,如:

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

  // _cache is library-private, thanks to
  // the _ in front of its name.
  static final Map<String, Logger> _cache =
      <String, Logger>{};

  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final logger = new Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  Logger._internal(this.name);

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

可使用 abstract 修饰符声明一个不能直接实例化的抽象类,抽象类能够包含抽象方法,抽象方法没有方法体,以分号结尾。
每一个类都隐式地定义了一个接口,该接口包含该类的全部实例成员及其实现的全部接口。能够经过 implements 子句实现一或多个接口,以逗号隔开。
使用 extends 建立子类,使用 super 引用父类,使用 @override 注解有意地重载成员。

在面向对象的语言中,多继承是个很是糟糕的设计,由于多继承会产生一系列问题,如:
多继承会使类之间的关系更加复杂。若是一个类有多个超类,超类又可能有多个超类,那么类之间的关系将会变得尤其复杂,你很难理清一个类的超类到底有哪些,继承关系又是怎样的。
多继承可能会出现菱形继承的状况,这种状况下会出现二义性,虽然 C++ 使用虚继承的方式能够暂时避免这种二义性,但会使程序可读性和可调试性变差。
因此现代的面向对象语言都不会容许多继承这种状况存在,其实继承的目的无非是为了重用并拓展类的功能,而经过继承来重用类体并非一个好的方式,首先继承会“白箱复用”,会将超类细节暴露给子类,无论子类愿不肯意。若是超类更改,其全部子类也不得不改变。并且继承在运行时的行为不易改变。
因此尽量少用继承,多用组合。继承是 is-a 的关系,而组合是 has-a 的关系。

Mixin 的概念源于 LISP 社区,在那里是指被设计用来与各个超类一块儿工做的类。对 Mixin 介绍比较详细的是 Mixins in Strongtalk 这篇论文。
一个类会隐式地定义一个 mixin, mixin 被类体定义,与类和它的超类构成函数关系,类实际上就是 mixin 的应用,是将其隐式定义的 mixin 应用于其超类的结果。Mixin应用函数应用 相似,从数学上来讲,一个 mixin M 能够当作是超类到子类的函数,也就是说,将 M 做用于超类 S,结果是新的 S 子类,在研究资料里一般写做 M ▷ S。所以对于 mixin 的组合来讲: (M_1 * M_2) ▷ S = M_1 ▷ (M_2 ▷ S)。 一个类隐式定义的 mixin 一般只会在给定超类定义处应用一次,因此为了让 mixin 可以应用不一样的超类,咱们须要独立于任何特定超类来声明 mixin,或者将类隐式定义的 mixin 剥离出来并在原始声明以外重用它。

Mixin 能够经过普通类定义的方式隐式定义,但要想提取 mixin 就必需要保证类不能声明构造器,这也避免了因为在继承链上传递构造器参数引发的各类并发症。

abstract class Collection<E> {
  Collection<E> newInstance();
  Collection<E> map((f) {
    var result = newInstance();
    forEach((E e) { result.add(f(e)); });
    return result;
  });
  void forEach(void f(E element)) {
    // ...
  }
  void add(E element) {
    // ...
  }
}

abstract class DOMElementList<E> = DOMList with Collection<E>; abstract class DOMElementSet<E> = DOMSet with Collection<E>; 复制代码

在这里,Collection 就是一个隐式声明了 mixin 的普通类,它没有构造器。而 DOMElementListDOMElementSet 类就是 mixin 的应用,它们被特殊的类声明格式定义,也就是经过 with 子句声明 mixin 到 父类的应用。这两个类也是抽象的,由于他们没有实现 Collection 类的抽象方法 newInstance()
也就是说,DOMElementList 就是 Collection mixin ▷ DOMList,而 DOMElementSet 就是 Collection mixin ▷ DOMSet
这样的好处是,Collection 类的代码能够在多个类层次上共享。咱们这里列举了两个类层次,一个是以 DOMList 为根的,一个是以 DOMSet 为根的。咱们不须要复制粘贴Collection 类的代码,而 Collection 的任何更改都会传播到这两个类层次中,这样也极大地解放了代码的维护。
上面的例子只是简单说明了一种形式的 mixin 应用,应用的结果是一个有名字的类。而另外一种形式,with 子句后跟上以逗号分隔的标识符列表,全部的标识符必须表示一个类,这种形式下,多个 mixin 会被组合并应用到 extends 子句的超类中,从而产生一个匿名超类,即:

class DOMElementList<E> extends DOMList with Collection<E> {
   DOMElementList<E> newInstance() => new DOMElementList<E>();
}

class DOMElementSet<E> extends DOMSet with Collection<E> {
  DOMElementSet<E> newInstance() => new DOMElementSet<E>();
}
复制代码

这里的 DOMElementList 再也不是 Collection mixin ▷ DOMList 的应用,而是一个新类,它的超类是这个应用。
若是 DOMList 有非平凡构造器,那么构造将会是这样:

class DOMElementList<E> extends DOMList with Collection<E> {
  DOMElementList<E> newInstance() => new DOMElementList<E>(0);
  DOMElementList(size): super(size);
}
复制代码

每一个 mixin 都有属于本身的被独立调用的构造器,超类也是如此。因为 mixin 的构造器不能被声明,对它的调用能够用 ; 语法省略,在底层实现时,这个调用会被放在初始化列表的开始处。
为了让多个 mixin 应用到一个类是而又不引入多个中间声明,可使用这种方便的语法糖:

class Person {
  String name;
  Person(this.name);
}

class Maestro extends Person with Musical, Aggressive, Demented {
  Maestro(name):super(name);
}
复制代码

在这里,Maestro 的超类就是这样的 mixin 应用:
Demented mixin ▷ Aggressive mixin ▷ Musical mixin ▷ Person
mixin 与普通继承同样,隐私性取决于其声明的位置。mixin 的应用的静态元素一样不能被继承使用,Dart 中静态元素是不能被继承的。
那 mixin 应用后的实例类型究竟是什么类型呢?毫无疑问,它必须是命名 mixin 类的子类型,也必须是原来类的子类型。原来的类有它本身的超类,为了确保指定的 mixin 应用 兼容原来的类,Dart 会给 with 子句的类额外的要求。
若是类 A 定义时的 with 子句是 mixin M,而 M 又是继承的 类 K,那么类 A 就必须支持类 K 的接口。

class S {
  twice(int x) => 2 * x;
}

abstract class I {
   twice(x);
}

abstract class J {
   thrice(x);
}
class K extends S implements I, J {
  int thrice(x) => 3* x;
}

class B {
  twice(x) => x + x;
}
class A = B with K; 复制代码

A 必须支持 K 的超类 S 的隐式接口,以确保 A 确实是 M 的子类型。K 必须实现 twice() 以知足 I 的须要,同时必须实现 thrice() 以知足 J 的须要,显然 K 已经知足了这些须要。
如今咱们声明了 A,咱们从 K 的 mixin 中 拿到了 thrice() 的实现,但这个 mixin 没有提供 twice() 的实现,幸运的是,B 有这个实现,因此总的来讲,A 确实知足了 I,J 和 S 的须要。
相反,对于这样的类 D:

class D {
   double(x) => x+x;
}

class E = D with K; 复制代码

会收到一个警告,由于 E 没有 twice() 方法,所以不知足 I 和 S,也就不能使用预期的 K 了。
若是一个类有泛型参数,那么它的 mixin 也必须有相同类型的参数。
在 1.13 版本以后,Mixin 能够继承普通类而不要求必定是 Object,Mixin 可使用 super.method()

操做符重载

能够经过 operator 关键字重载包括 +>== 在内的操做符,但要注意,重载 == 操做符的同时也要重载 hashCodegetter 方法:

class Person {
  final String firstName, lastName;

  Person(this.firstName, this.lastName);

  // Override hashCode using strategy from Effective Java,
  // Chapter 11.
  @override
  int get hashCode {
    int result = 17;
    result = 37 * result + firstName.hashCode;
    result = 37 * result + lastName.hashCode;
    return result;
  }

  // You should generally implement operator == if you
  // override hashCode.
  @override
  bool operator ==(dynamic other) {
    if (other is! Person) return false;
    Person person = other;
    return (person.firstName == firstName &&
        person.lastName == lastName);
  }
}
复制代码

枚举类型

使用 enum 关键词定义枚举类型,枚举类型的值有一个 index getter,枚举值的列表能够经过它的 values 常量获取,枚举支持 switch 语句,你不能继承,mix in 或者实现枚举类,不能显式实例化枚举:

enum Color { red, green, blue }
assert(Color.red.index == 0);
List<Color> colors = Color.values;
复制代码

library 和 可见性

每一个 Dart app 都是一个 library,即便它没使用 library 指令,library 不只提供 API,仍是个隐私单元,如下划线(_)开头的标识符只在当前 library 内可见。
使用 import 指令能够指定在另外一个 library 范围内如何使用另外一个 library 的命名空间。对于内置的 library,URI 是特定的 dart: scheme,而其余的 URI 能够是文件系统路径或者 package: scheme:

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

若是两个 library 的标识符冲突,可使用前缀加以区分:

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

// Uses Element from lib1.
Element element1 = new Element();

// Uses Element from lib2.
lib2.Element element2 = new lib2.Element();
复制代码

若是只想引入 library 的部分功能,须要使用 showhide 指定:

// Import only foo.
import 'package:lib1/lib1.dart' show foo;

// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
复制代码

若是想要懒加载某个 library,先在 import 时经过 deferred as 指定 library 的标识符,而后在须要使用它的时候调用标识符的 loadLibrary():

import 'package:greetings/hello.dart' deferred as hello;
...
Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}
复制代码

此时 await 关键字会暂停程序执行,直到 library 加载成功,能够屡次调用 loadLibrary(),由于 library 只会加载一次。

异步

Dart 为异步提供了 FutureStream 两个类,Future 就像未来某个时刻提供结果的承诺,Stream 是获取值序列(例如事件序列)的一种方式。
不少时候没有必要直接使用 Future 或 Stream API,而是使用 asyncawait 关键字进行异步编程,这样更容易理解,并且代码看起来也更像同步代码。
await 必须在异步函数中使用,用来等待异步函数返回的结果,如:

Future checkVersion() async {
  var version = await lookUpVersion();
  // Do something with version
}
复制代码

虽然异步函数能够执行耗时操做,但它不用等待耗时操做完成,这个异步函数会执行直到遇到第一个 await 表达式,而后返回 Future 对象,在 await 表达式执行完后再继续执行。
能够在一个异步函数里 await 屡次,也能够 try/catch await 的代码。
await expression 语句中,expression 的值一般是个 Future,若是不是,那么它会被自动包装成 Future。await expression 会暂停其以后代码的执行,直到返回的对象可用。
若是异步函数不须要返回有用值,能够那么返回类型应该是 Future<void>
能够用 await for 异步等待 stream 中的全部结果,但不能是 UI 事件流这样的 stream,由于这样的 stream 是无限的。

生成器函数

Dart 内置了两种生成器函数,同步的生成器函数返回 Iterable 对象,异步的生成器函数返回 Stream 对象,使用 sync*/async* 指定同步异步,使用 yield 交付值,还能够经过 yield* 递归生成:

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

注解

自定义注解就像声明普通类同样:

library todo;

class Todo {
  final String who;
  final String what;

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

Effective Dart

代码风格

  • 类、枚举、注解、typedef 要使用大驼峰命名法。
  • library 和 源文件的命名(由于有些文件系统并非大小写敏感的)、import 前缀(即 as 子句)要所有小写并用下划线分隔。
  • 常量名等要用小驼峰命名法。
  • 除了两个字母的首字母缩写词,其余的首字母缩写词和缩写词应该和普通单词同样进行标识符大小写。如:
    HttpConnectionInfo
    uiHandler
    IOStream
    HttpRequest
    Id
    DB
    复制代码
  • 每行尽可能不超过80个字符。

使用方式

  • 引入本身库 lib 目录下的 library时,要使用相对路径。
  • 一行放不下的字符串不要使用加号拼接,直接相邻两行书写便可。对于表达式的值和其余字符串组合的字符串,也不要使用加号拼接,直接使用 ${expression} 插值便可。
  • 使用 .length 判断集合是否为空可能会很是慢,因此使用 .isEmpty.isNotEmpty 判断集合状况是最好的选择,更快也更直观。
  • 拷贝集合有两种方式:
    var copy1 = iterable.toList();
    var copy2 = new List.from(iterable);
    复制代码
    第一种能够保存原始对象的类型参数,而第二种的运行时类型是 dynamic 的,不过能够经过 new List<int>.from(numbers) 这样的方式指定。
  • 过滤集合中的某类元素时应该使用 whereType():
    var objects = [1, "a", 2, "b", 3];
    var ints = objects.whereType<int>();
    复制代码
  • 转换 iterable 或 stream 的类型时,尽可能不要用 cast(),由于该方法会返回 lazy 集合而且每次操做都要检查元素类型,若是集合元素和元素操做特别多那么 lazy 验证和打包的开销会特别大,因此 stuff.toList().cast<int>() 彻底能够用 new List<int>.from(stuff) 替代,stuff.map((n) => 1 / n).cast<double>() 彻底能够用 stuff.map<double>((n) => 1 / n) 替代。
  • 善于使用 => 简化简单的代码逻辑,如:
    num get x => center.x;
    set x(num value) => center = new Point(value, center.y);
    bool isReady(num time) => minTime == null || minTime <= time;
    复制代码

参考

相关文章
相关标签/搜索