Dart官方文档阅读笔记

由于准备入坑Flutter,所以Dart语言也是必学的。花了两天时间过了一遍官方入门文档,对于如果拥有其他面向对象语言基础,并且英文比较好的同学,上手Dart是非常快的(Swift,Dart,Kotlin三胞胎🤣)。本文总结了Dart的一些语法特点便于以后翻阅,比较基础的就跳过啦。

基本概念

  • 所有的变量均为对象,所有的对象都是一个类的实例,包括数字,方法和空值(null)。所有的对象继承自Object类。
  • Dart是强类型语言,并且可以推断变量类型即使没有声明。
  • Dart无继承关键字(public, protected, and private),使用_开头的成员变量默认为私有属性。

变量

  • 未初始化的变量默认为null,即使是数字这种基本类型也为null,因为在Dart中所有变量均为对象类型。
  • 一个final修饰的变量无法再次被赋值。
  • 一个const修饰的值在编译期间就已经确定,无法被改变。

内建类型

Dart对于以下数据类型有特殊处理:

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

可使用字面量语法初始化这些类型的对象

数字

Dart中的数字有两种类型:int和double

字符串转数字:

var one = int.parse('1');

数字转字符串:

String oneAsString = 1.toString();

(数字字面量直接用点语法,这糖真甜😁)

字符串

  • Dart字符串为UTF-16字符序列,可使用””或者’’创建
  • 使用${expression}将表达式放入一个字符串中
  • 使用==判断两个字符串是否等同,若两个字符串的字符序列相同,则两者等同
  • 使用+合并字符串
  • 使用r创建一个字符串生值

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

布尔值

  • if和assert只可接收布尔值

数组

  • 使用Lists表示,可自动推断元素类型
  • 在数组前加const表示一个不可变数组,为编译器常量

    var constantList = const [1, 2, 3];

字典

  • 使用Maps表示,可自动推断元素类型
  • 加const代表不可变字典

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

字符

  • Dart字符由UFT-32字符集构成,由于字符(UFT-32)与字符串(UTF-16)为不用字符集,相互转化时需要特殊语法
  • 表示一个Unicode字符:\uXXXX,超过4位:\u{1f600}

方法

  • Dart中的方法是一个Funciton对象
  • Dart返回简写语法:

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

  • 参数默认值

    void enableFlags({bool bold = false, bool hidden = false}) {...}

  • 函数可作为参数传入另一个函数
  • 匿名函数

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

操作符

  • 使用as做强制类型转换, 可能会抛出异常

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

  • 使用is判断对象类型

    1
    2
    3
    4
    if (emp is Person) {
    // Type check
    emp.firstName = 'Bob';
    }
  • expr1 ?? expr2:若expr1非空,返回该值,否则返回expr2

级联调用

  • 使用..对同一个对象连续做一系列操作,包括函数调用,无需声明临时变量

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

异常

  • 未捕获的异常会造成程序终止
  • Dart提供ExceptionError两种类型的异常,也可以抛出一个非空对象作为异常

    throw 'Out of llamas!';

  • 使用on捕获特定类型的异常,使用catch捕获未被指定的异常

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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');
    }
  • 重复传播异常时使用rethrow关键字

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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.
    }
    }
  • 使用Finally可以保证即使有异常产生也保证代码继续运行

    1
    2
    3
    4
    5
    6
    try {
    breedMoreLlamas();
    } finally {
    // Always clean up, even if an exception is thrown.
    cleanLlamaStalls();
    }

  • 使用?.可以避免调用者为空值时产生异常

    1
    2
    // If p is non-null, set its y value to 4.
    p?.y = 4;

构造函数

  • 构造函数的语法糖:

    1
    2
    3
    4
    5
    6
    7
    class Point {
    num x, y;

    // Syntactic sugar for setting x and y
    // before the constructor body runs.
    Point(this.x, this.y);
    }
  • 如果未声明构造函数,那么会隐式的生成一个无初始化参数的构造函数

  • 构造函数不会被继承
  • 命名构造函数,同样不会被继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Point {
    num x, y;

    Point(this.x, this.y);

    // Named constructor
    Point.origin() {
    x = 0;
    y = 0;
    }
    }
  • 子类的构造函数默认会在首部调用父类的默认构造函数

  • 若需要触发父类的非默认构造函数,则可在函数名后使用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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');
    }
    }
  • 子类也可直接使用父类的构造函数

    1
    2
    3
    4
    class Employee extends Person {
    Employee() : super.fromJson(getDefaultData());
    // ···
    }
  • 语法糖:可以在构造函数体调用之前出事偶会实例变量的值

    1
    2
    3
    4
    5
    6
    7
    // 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)');
    }
  • 语法糖:构造函数体调用前可使用断言验证参数合法性

    1
    2
    3
    Point.withAssert(this.x, this.y) : assert(x >= 0) {
    print('In Point.withAssert(): ($x, $y)');
    }
  • 重定向构造函数(使用已有的构造函数生成另一个构造函数)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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);
    }
  • 若对象不会被改变,可使用常量构造函数,需保证所有的实例变量均为编译期常量

    1
    2
    3
    4
    5
    6
    7
    8
    class ImmutablePoint {
    static final ImmutablePoint origin =
    const ImmutablePoint(0, 0);

    final num x, y;

    const ImmutablePoint(this.x, this.y);
    }
  • 工厂模式构造函数,如果调用构造函数时不一定需要生成一个实例时,可使用关键字factory

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    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 = Logger._internal(name);
    _cache[name] = logger;
    return logger;
    }
    }

    Logger._internal(this.name);

    void log(String msg) {
    if (!mute) print(msg);
    }
    }

方法

  • Dart会为对象的属性隐式生成getter和setter方法
  • 抽象方法只可存在于抽象类中
  • 抽象类不可被实例化,一般用于定义接口
  • 声明抽象方法只需用;代替方法体

    1
    2
    3
    4
    5
    abstract class Doer {
    // Define instance variables and methods...

    void doSomething(); // Define an abstract method.
    }
  • 类可以继承多个接口

  • 使用@override重写成员函数

    1
    2
    3
    4
    5
    class SmartTelevision extends Television {
    @override
    void turnOn() {...}
    // ···
    }
  • 支持操作符重写

  • 重写==操作符时同时需要重写hashcode方法(getter)
  • 重写nosuchMethod方法后,当调用一个不存在的方法时会被触发

枚举

  • 使用values获取枚举所有值的数组

    1
    2
    List<Color> colors = Color.values;
    assert(colors[2] == Color.blue);
  • 使用switch语句时,如果没有处理所有的枚举值则会收到警告

  • 枚举类型有如下限制:
    1. 不可继承,混入
    2. 不可实例化

混入

  • 混入可在多个类的体系中复用一个类的代码
  • 使用关键词with,支持混入多个类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Musician extends Performer with Musical {
    // ···
    }

    class Maestro extends Person
    with Musical, Aggressive, Demented {
    Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
    }
    }
  • 使用on指代一个混入类的特定类型

    1
    2
    3
    mixin MusicalPerformer on Musician {
    // ···
    }

类变量和类方法

  • 使用static指代类变量和类方法
  • 类变量不可访问this
  • 类变量是编译期常量,可作为函数参数传入

泛型

  • 泛型类型是具体化的,运行时携带了完整的类型信息,可使用is判断其具体类型

    1
    2
    3
    var names = List<String>();
    names.addAll(['Seth', 'Kathy', 'Lars']);
    print(names is List<String>); // true
  • 使用extends约束泛型类型

    1
    2
    3
    4
    5
    6
    class Foo<T extends SomeBaseClass> {
    // Implementation goes here...
    String toString() => "Instance of 'Foo<$T>'";
    }

    class Extender extends SomeBaseClass {...}
  • 泛型函数:

    1. 返回值可为泛型 (T)
    2. 参数可为泛型 (List)
    3. 临时变量为泛型 (T tmp)

      1
      2
      3
      4
      5
      6
      T first<T>(List<T> ts) {
      // Do some initial work or error checking, then...
      T tmp = ts[0];
      // Do some additional checking or processing...
      return tmp;
      }

  • 使用package:做唯一标识

    import 'package:test/test.dart';

  • 库名标识冲突时使用as

    1
    2
    3
    4
    5
    6
    7
    8
    import 'package:lib1/lib1.dart';
    import 'package:lib2/lib2.dart' as lib2;

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

    // Uses Element from lib2.
    lib2.Element element2 = lib2.Element();
  • 引入库的某一部分

    1
    2
    3
    4
    5
    // Import only foo.
    import 'package:lib1/lib1.dart' show foo;

    // Import all names EXCEPT foo.
    import 'package:lib2/lib2.dart' hide foo;
  • 使用deferred as懒加载:

    import 'package:greetings/hello.dart' deferred as hello;

    当需要加载库时,调用loadLibrary()

    1
    2
    3
    4
    Future greet() async {
    await hello.loadLibrary();
    hello.printGreeting();
    }
    • await可保证库加载完成再执行后续代码
    • loadLibrary()可调用多次,但库只会被加载一次

异步

  • 使用asyncawait支持异步编程
  • 要使用await,其方法须带有async关键字
  • 可以使用trycatchfinally来处理await的异常
  • 异步方法声明

    1
    2
    3
    4
    5
    checkVersion() async {
    // ...
    }

    lookUpVersion() async => /* ... */;

    异步方法在方法体执行之前就返回了

  • 在循环中使用异步

    1
    2
    3
    await for (variable declaration in expression) {
    // Executes each time the stream emits a value.
    }

    expression返回的值必须是stream类型的

生成器

  • 当需要延迟产生一系列的值是使用generator,dart提供两种类型:
    1. 同步-生成器,返回一个Iterable对象
    2. 异步-生成器,返回一个Stream对象
  • 同步-生成器,方法体声明为sync*,使用yield发送值

    1
    2
    3
    4
    Iterable<int> naturalsTo(int n) sync* {
    int k = 0;
    while (k < n) yield k++;
    }
  • 异步-生成器,方法体声明为async*,使用yield发送值

    1
    2
    3
    4
    Stream<int> asynchronousNaturalsTo(int n) async* {
    int k = 0;
    while (k < n) yield k++;
    }
  • 使用yield*递归发送值

    1
    2
    3
    4
    5
    6
    Iterable<int> naturalsDownFrom(int n) sync* {
    if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
    }
    }

Callable classes(可调用的类)

  • 类实现call()方法后可作为函数被调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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');
    }

Isolates

Dart不使用多线程,所有的 Dart 代码在 isolates 中运行而不是线程。 每个 isolate 都有自己的堆内存,并且确保每个 isolate 的状态都不能被其他 isolate 访问