企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] # 简介 [dart.dev](https://dart.dev/) [dart.cn](https://dart.cn/) 项目地址:https://github.com/dart-lang 目前市面上几乎没有 **Dart 2** 相关的书籍,如果你想学习Flutter ,那么Dart ,也就成为了必须要具备的基础知识,官网就是个不错的地方,资料也很详细~ [dart-cheat-sheet](https://github.com/Temidtech/dart-cheat-sheet) [Flutter-Cheat-Sheet](https://github.com/Temidtech/Flutter-Cheat-Sheet) ## 在线编写和运行 Dart https://dartpad.dartlang.org/ https://dartpad.cn/ # Dart Libraries 和 第三方包管理 首先我们需要知道,Dart 是依赖于库的,从而实现相应的功能; | 库名 | 作用 | | --- | --- | | `dart:core` | 内置类型,集合和其他核心功能。**每个Dart程序默认会自动导入该库** | | `dart:async` | 异步编程通常使用回调函数,但Dart提供了备选方案:[Future](https://api.dartlang.org/stable/dart-async/Future-class.html) 和 [Stream](https://api.dartlang.org/stable/dart-async/Stream-class.html) 对象。未来就像是对将来某个时候提供结果的承诺。 Stream是一种获取值序列的方法,例如事件。 Future,Stream等等都在 dart:async 库([API参考](https://api.dartlang.org/stable/dart-async/dart-async-library.html))中。 | | `dart:math` | `dart:math` 库(API参考)提供常见功能, 如正弦和余弦,最大值和最小值,以及常量,如pi 和 e。 Math库中的大多数功能都是作为顶级函数实现的。 | | `dart:convert` | `dart:convert` 库(API参考)具有JSON和UTF-8转换器,并支持创建其他转换器。 [JSON](https://www.json.org/) 是一种用于表示结构化对象和集合的简单文本格式。 [UTF-8](https://en.wikipedia.org/wiki/UTF-8) 是一种常见的可变宽度编码,可以表示Unicode字符集中的每个字符。 | 当然还有其他的针对特定平台的包,如:[dart:io](https://www.dartlang.org/dart-vm/io-library-tour) 、[dart:html](https://webdev.dartlang.org/guides/html-library-tour) 等,你可以在这里查看[所有包的api](https://api.dartlang.org/stable)。 Flutter 包的 api:https://docs.flutter.io/ 第三方包搜索:https://pub.dartlang.org/ ## 库和可见性 `import` 和 `library`指令可以帮助您创建模块化和可共享的代码库。库不仅提供api,而且包含隐私部分:以下划线(`_`)开头的标识符仅在库中可见。**每个Dart应用程序都是一个库**,即使它不使用`library`指令。 可以使用包来分发库。[pub](https://www.dartlang.org/tools/pub) 是SDK中包含的**包管理器**。 ## 使用库 导入的唯一必需参数是指定库的URI。对于内置库,URI具有特殊的`dart:` 格式(scheme)。对于其他库,您可以使用文件系统路径或 `package:` 格式。 `package:` 格式指定由包管理器(如 pub 工具)提供的库。例如: ```dart import 'package:meta/meta.dart'; ``` ## 指定库前缀 通过 `as` 来制定库前缀, ``` // lib1,和lib2 都有 Element 怎么办? import 'package:lib1/lib1.dart'; import 'package:lib2/lib2.dart' as lib2; // 来自lib1的 Element Element element1 = Element(); // 来自lib2 的 Element lib2.Element element2 = lib2.Element(); ``` ### 使用库中部分内容 ``` // 只导入 foo。 import 'package:lib1/lib1.dart' show foo; // 导入所有名称,除了foo。 import 'package:lib2/lib2.dart' hide foo; ``` ## 懒加载一个库 要延迟加载库,首先必须使用 `deferred as` 导入库。 您可以在一个库上多次调用`loadLibrary()`,而不会出现任何问题。该库只加载一次。 ```dart import 'package:greetings/hello.dart' deferred as hello; Future greet() async { // 通过await ,暂停等待库的加载。有关async和await的详细信息,请参阅[异步支持](https://www.dartlang.org/guides/language/language-tour#asynchrony-support) await hello.loadLibrary(); hello.printGreeting(); } ``` ## 创建自己的库程序包 你可以[创建自己的库](https://www.dartlang.org/guides/libraries/create-library-packages)。 如何组织库源代码。 如何时使用 `export` 指令。 何时使用 `part` 指令。 [其他常用库](https://www.dartlang.org/guides/libraries/useful-libraries) # [基础语法](https://www.dartlang.org/guides/language/language-tour) ## [关键字](https://www.dartlang.org/guides/language/language-tour#keywords) ![](https://box.kancloud.cn/e92dfaafda7899d97c18b6db64be0a75_556x496.png) 带有字样1的是内置标志符号,带有2字样的是Dart2新增的用于支持异步的关键字,其他的都是保留字! ## 语法规则 * 通过 `var` 声明变量而不指定类型; * Dart 是强类型语言,但是声明变量时也可以不指定类型,因为**Dart 可以自动推断**, ``` var number = 16; ``` 在上面的例子中,变量number 就被自动推断为`int` 类型 * 如果对象不限于单个类型,请[按照设计准则](https://www.dartlang.org/guides/language/effective-dart/design#do-annotate-with-object-instead-of-dynamic-to-indicate-any-object-is-allowed)指定 `Object` 或者 `dynamic` 类型。 ``` dynamic name = 'Bob'; ``` * Dart中所有的变量包括数字、函数和`null` 都是对象,每一个 对象都是一个类的实例,他们都继承于 `Object`。 * Dart 支持泛型,如`List<int>`表示集合元素类型为整型、`List<dynamic>` 表示元素类型为任何类型 * Dart 除了支持我们常见的静态函数(类直接调用)和普通函数(对象调用)外,同时也支持顶级函数如`main()` 和嵌套函数(函数里面的函数,也叫本地函数) * 与函数类似,Dart 也支持顶级变量,同时支持静态变量和实例变量,实例变量也被叫做字段或属性 * 和 java 不同,Dart 没有 `public`、`protected`、`private`权限修饰符,在 Dart 里面的以下划线`_`开头的标志符表示是私有的 * Dart 里面的标志符号由`_`、字母和 数字组成,只能以`_`或者字母开头 * 要注意区分表达式和语句的不同,如`var a = 3 + 2`;整体是一个赋值语句,`=` 右边部分即 `3 + 2` 是一个表达式 * Dart 工具会向你发出两种类型的提醒:警告和错误。警告代表你的代码可能有问题,但是不会阻止程序的运行;**错误分为编译错误和运行错误**,前者会阻止程序的运行,后者则会在程序运行时抛出异常! ## Final 和 Const 在 Dart 中,未初始化的变量拥有一个默认的初始化值:`null`。即便数字也是如此,因为在 Dart 中一切皆为对象,数字也不例外。 `const` 表示编译时常量,即在代码还没有运行时我们就知道它声明变量的值是什么; `final` 不仅有 `const` 的编译时常量的特性,最重要的它是运行时常量,并且 `final` 是惰性初始化,即在运行时第一次使用前才初始化 ```dart const int a = 1; final int a = 8; const b = false; const c = a; const d = 5 * 3; final x = new DateTime.now(); // 正确 const x = new DateTime.now(); // 错误 final y = sin(90); // 正确 const y = sin(90); // 错误 ``` ## 内置类型 ### Numbers 1. `int` 整数值不大于64位,具体取决于平台。 2. `double` 64位(双精度)浮点数,由IEEE754标准指定。 **`int` 和 `double` 都是 `num` 的子类型** 示例: ``` int x = 1; int hex = 0xDEADBEEF; double y = 1.1; double exponents = 1.42e5; ``` ### Booleans Dart是强布尔类型检查,只有当值是 `true` 是才为真,其他都是 `false`,声明时用 `bool` ### Strings ``` //拼接字符串 var s5 = 'dd''ff' "ee"; //上面的方法等价于 var s6 = 'dd' + 'ff' + "ee"; // 多行文本使用方法,三单引号 或者 双引号 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 $height.'; ``` ### Lists 在 Dart 语言中,具有一系列相同类型的数据被称为 List 对象。 Dart List 对象类似JavaScript 语言的 array 对象。 ```dart var list = [1, 2, 3]; //分析器自动推断list为List<int>,所以里面不能加入其他类型的元素 print(list.length); //3 list[1] = 11; print(list.toString()); //[1, 11, 3] var constantList = const [1, 2, 3]; // constantList[1] = 1; //因为前面的赋值使用了const 所以这句会报错. constantList= [5]; //这样可以 ``` ### Maps map 是一个包含 `key` 和 `value` 的对象,`key` 不能重复 ``` var gifts = { // Key: Value 'first': 'partridge', 'second': 'turtledoves', 'fifth': 'golden rings' }; var nobleGases = { 2: 'helium', 10: 'neon', 18: 'argon', }; var gifts2 = new Map(); gifts['first'] = 'partridge'; gifts['second'] = 'turtledoves'; gifts['fifth'] = 'golden rings'; var nobleGases2 = new Map(); nobleGases[2] = 'helium'; nobleGases[10] = 'neon'; nobleGases[18] = 'argon'; //获取value print(gifts2['first']);// partridge; print(gifts2.length);// 3 ``` ### runes Dart 中 使用 `runes` 来获取UTF-32字符集的字符。String的 `codeUnitAt` 和 `codeUnit` 属性可以获取UTF-16字符集的字符 ```dart 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)); ``` ### symbols symbol 字面量是编译时常量,在标识符前面加`#`。如果是动态确定,则使用`Symbol` 构造函数,通过`new`来实例化.你可能永远也用不到 `Symbol ` ``` print(#s == new Symbol('s'));//true ``` # 函数 ```dart bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null; //箭头函数,`=>` 实际为 `{ return expr; }` // 可选命名参数 /// Sets the [bold] and [hidden] flags ... void enableFlags({bool bold, bool hidden}) {...} // 可选位置参数 String say(String from, String msg, [String device]) { var result = '$from says $msg'; if (device != null) { result = '$result with a $device'; } return result; } say('Bob', 'Howdy') ; ``` ## 顶级函数 以下是测试顶级函数,静态方法和实例方法的示例: ```dart 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); } ``` ## 可选参数 * 名称可选参数,调用一个函数时,你可以指定参数名称,语法 `paramName: value`。 举例: ```dart enableFlags(bold: true, hidden: false); ``` 那如何定义这样的函数呢?使用 `{param1, param2, …}`来指定名称参数: ```dart /// Sets the [bold] and [hidden] flags ... void enableFlags({bool bold, bool hidden = false}) {...} ``` **Flutter** 中实例创建表达式很复杂,所以 widget 构造时使用名称可选参数。这使得 实例创建表达式 更易读。 你可以使用`@required`注解来标注名称参数,在任何 dart 代码中,这表示这是一个 必填的参数。 举例: ```dart const Scrollbar({Key key, @required Widget child}) ``` 当一个`Scrollbar`被构建的时候,如果缺失 `child`字段,语法会报错。 `Required`定义在 [meta](https://pub.dev/packages/meta) 包中。可以直接导入 `package:meta/meta.dart`,也可以导入其他包来暴露 `meta`, 比方说 `package:flutter/material.dart`。 * 可选的位置参数,用`[]`它们标记为可选的位置参数: ```dart String say(String from, String msg, [String device]) { var result = '$from says $msg'; if (device != null) { result = '$result with a $device'; } return result; } say('Bob', 'Howdy'); // 不带可选参数调用,结果是: Bob says Howdy say('Bob', 'Howdy', 'smoke signal'); //结果是:Bob says Howdy with a smoke signal ``` ## 参数默认值 下面的示例定义了一个函数 `doStuff()`,它为 `list` 参数指定了默认列表,为 `gifts` 参数指定了默认映射。 ```dart 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` 函数 每一个程序都要有一个主函数,它是 `app` 的入口,无返回值,有一个 `List<String>`类型的可选参数。 下面是一个web app的主函数: ``` void main() { querySelector('#sample_text_id')//获取页面上的button对象 ..text = 'Click me!'//设置文本 ..onClick.listen(reverseText);//设置监听 } ``` 注:`..`是一种级联操作符,它返回调用者本身,这样就可以连续调用同一个对象上的多个方法,实现链式操作! 下面是一个带参的主函数,需要命令行操作: ``` //运行程序:dart args.dart 1 test //args.dart 是程序文件名 1 test 是参数 void main(List<String> arguments) { print(arguments.length);//2 print(int.parse(arguments[0]));//1 print(arguments[1]);//test } ``` ## 函数对象 * 函数可以作为一个参数 ```dart void printElement(int element) { print(element); } var list = [1, 2, 3]; // Pass printElement as a parameter. list.forEach(printElement);// 1 2 3 ``` * 函数可以赋值给一个变量 ```dart var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!'; print(loudify('hello'));//!!! HELLO !!! ``` 上面用到了匿名函数,下面的将会介绍。 ## 匿名函数 大多数函数都有名字,如 `main()`,没有名字的函数被称为匿名函数,有时也叫做`闭包`或 `lambda ` ``` var list = ['apples', 'bananas', 'oranges']; list.forEach((item) { print('${list.indexOf(item)}: $item'); }); // 上面的函数可以改写为箭头函数 list.forEach( (item) => print('${list.indexOf(item)}: $item')); // 0: apples 1: bananas 2: oranges ``` ## 作用域 ``` 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); } } } ``` ## 词法闭包 ```dart /// Returns a function that adds [addBy] to the /// function's argument. Function makeAdder(num addBy) { return (num i) => addBy + i; // 返回一个函数, addBy会被记得 } 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); } ``` ## 函数返回值 函数没有明确返回值时,`return null` 会被隐式添加。 ``` foo() {} ``` # 运算符 ## `==` 如果 x 或 y 为 `null` ,如果两者都为`null` 返回`true`,如果只有一个为`null` 返回`false`。 判断两个对象完全相同,使用 [`identical()`](https://api.dartlang.org/stable/dart-core/identical.html) ## 类型检测运算符 `as`、`is`、`is!` 在运行时检测类型。 ``` if (emp is Person) { // Type check emp.firstName = 'Bob'; } // 您可以使用 as 运算符缩短上面的代码,如果 emp 是 null 或不是Person,as 检测到错误会抛出异常 (emp as Person).firstName = 'Bob'; ``` ## `??`操作符 如果第一个表达式值不为 `null`,返回它的值,否则就返回第二个值; ``` //如果b为 null,则将值赋给 b;否则,b 保持不变 b ?? = value; String playerName(String name) => name ?? 'Guest'; // 等价于下面:Slightly longer version uses ?: operator. String playerName(String name) => name != null ? name : 'Guest'; ``` ## `..`级联运算符(仅为特殊语法) * 级联运算符`..`允许您对同一个对像进行一系列的操作。除了函数调用之外,还可以访问同一对象上的字段。这通常会为您节省创建临时变量的步骤,并允许您编写更流畅的代码。 ```dart querySelector('#confirm') // Get an object. ..text = 'Confirm' // Use its members. ..classes.add('important') ..onClick.listen((e) => window.alert('Confirmed!')); ``` * 上述例子相对于: ```dart var button = querySelector('#confirm'); button.text = 'Confirm'; button.classes.add('important'); button.onClick.listen((e) => window.alert('Confirmed!')); ``` * 级联符号也可以嵌套使用。 例如: ```dart final addressBook = (AddressBookBuilder() ..name = 'jenny' ..email = 'jenny@example.com' ..phone = (PhoneNumberBuilder() ..number = '415-555-0100' ..label = 'home') .build()) .build(); ``` * 当返回值是void时不能构建级联。 例如,以下代码失败: ```dart var sb = StringBuffer(); sb.write('foo') // 返回void ..write('bar'); // 这里会报错 ``` **注意: 严格地说,级联的..符号不是操作符。它只是Dart语法的一部分。** > https://www.jianshu.com/p/9e5f4c81cc7d ## 有条件的成员访问符 ``` foo?.bar //当foo为null时,foo?.bar 为null ``` ## 流程控制语句 ``` if (isRaining()) { you.bringRainCoat(); } else if (isSnowing()) { //if else 语句 you.wearJacket(); } else { car.putTopDown(); } var callbacks = []; for (var i = 0; i < 2; i++) { //for 语句 callbacks.add(() => print(i)); } callbacks.forEach((c) => c()); //forEach 语句 var collection = [0, 1, 2]; for (var x in collection) { //for in 语句 print(x); // 0 1 2 } for (int i = 0; i < candidates.length; i++) { var candidate = candidates[i]; if (candidate.yearsExperience < 5) { continue; //跳入下一次循环 } candidate.interview(); } var command = 'CLOSED'; switch (command) { case 'CLOSED': executeClosed(); continue nowClosed; // 继续在nowClosed标签上执行。 nowClosed: case 'NOW_CLOSED': //CLOSED 和 NOW_CLOSED都会执行该语句 executeNowClosed(); break; } ``` ## 断言 第一个参数,可以是任何返回bool值的表达式,第二个参数,是显示的信息。 成功继续执行,如果为false,则断言失败并抛出异常([AssertionError](https://api.dartlang.org/stable/dart-core/AssertionError-class.html))。 ``` assert(urlString.startsWith('https'), 'URL ($urlString) should start with "https".'); ``` ## 异常 ``` throw 'Out of llamas!'; //抛出任何形式的异常。 try { breedMoreLlamas(); } on OutOfLlamasException { // A specific exception buyMoreLlamas(); } on Exception catch (e) { print('Exception details:\n $e'); } catch (e, s) { //第一个是抛出的异常,第二个是堆栈跟踪( StackTrace 对象 )。 print('Exception details:\n $e'); print('Stack trace:\n $s'); } finally { // 无论是否抛出异常,确保某些代码总能够运行,请使用 finally子句 cleanLlamaStalls(); // Then clean up. } ``` 要部分处理异常,同时不停止程序的执行,请使用 `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}.'); } } ``` # Class ## 常量构造函数 ```dart // 常量上下文,const 为可选 const pointAndLine = { 'point': [ImmutablePoint(0, 0)], 'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)], }; var a = const ImmutablePoint(1, 1); // Creates a constant var b = ImmutablePoint(1, 1); // 此时没有const 修饰,又不在常量上下文中 assert(!identical(a, b)); // NOT the same instance! ``` ## 对象运行时类型 `runtimeType`,该属性返回一个[Type](https://api.dartlang.org/stable/dart-core/Type-class.html) 对象 ``` print('The type of a is ${a.runtimeType}'); ``` ## 构造函数 **子类不会从其父类继承构造函数,但自身也会有默认构造函数**。默认构造函数没有参数,并在超类中调用无参数构造函数。 ``` class Point { num x, y; //在构造函数体运行之前, 用于设置x和y的语法糖 Point(this.x,this.y); } ``` ## 调用父类命名构造函数 ``` class Person { String firstName; final num x; final num y; final num distanceFromOrigin; Person.fromJson(Map data) { print('in Person'); } // 初始化那三个 final 字段 Point(x, y) : x = x, y = y, distanceFromOrigin = sqrt(x * x + y * y); // Delegates to the main constructor. Point.alongXAxis(num x) : this(x, 0); } class Employee extends Person { // Person没有默认构造函数;调用父类命名构造函数。 Employee.fromJson(Map data) : super.fromJson(data) { print('in Employee'); } } main() { var emp = new Employee.fromJson({}); // Prints: // in Person // in Employee } // ============ class ImmutablePoint { static final ImmutablePoint origin = const ImmutablePoint(0, 0); final num x, y; // 常量构造函数 const ImmutablePoint(this.x, this.y); } // ============ 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` 自定义构造函数逻辑,但是无法访问 this 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); } } ``` ## getter 和 setter ``` class Rectangle { num left, top, width, height; Rectangle(this.left, this.top, this.width, this.height); // 定义两个计算属性:right 和 bottom。 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); } ``` ## 抽象方法 `abstract`修饰符 ``` abstract class Doer { // 定义实例变量和方法...... //... void doSomething(); // 定义了一个抽象方法,所以类必须为抽象类 } class EffectiveDoer extends Doer { void doSomething() { // 这里继承并实现对应的抽象方法 } } ``` ## `implements` 一个类实现多个接口: ``` // 一个 person 类, 隐式接口包含 greet(). class Person { // In the interface, but visible only in this library. final _name; // Not in the interface, since this is a constructor. Person(this._name); // In the interface. 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())); } ``` ## `extends` 可以在子类中通过 `super`,引用父类。 ``` class Television { void turnOn() { _illuminateDisplay(); } // ··· } class SmartTelevision extends Television { void turnOn() { super.turnOn(); _bootNetworkInterface(); } // ··· } ``` ## `@override` 对于所有 Dart 代码有两种可用注解:`@deprecated`和`@override`。 使用 `@override` 注解(annotation) 表明要覆盖的成员。 ``` class SmartTelevision extends Television { @override void turnOn() {...} // ··· } ``` > [元数据](https://www.dartcn.com/guides/language/language-tour#%E5%85%83%E6%95%B0%E6%8D%AE) ## [covariant](https://www.dartlang.org/guides/language/sound-problems#the-covariant-keyword) 在类型安全的代码中,缩小方法参数或实例变量的类型,可以使用 `covariant` (协变) 关键字 ``` class Animal { void chase(Animal x) { ... } //这里是父类型 } class Mouse extends Animal { ... } class Cat extends Animal { void chase(covariant Mouse x) { ... } //通过用子类型覆盖参数的类型来收紧类型 } ``` ## 可重写运算符 ``` 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); // Operator == and hashCode not shown. For details, see note below. // ··· } void main() { final v = Vector(2, 3); final w = Vector(2, 2); assert(v + w == Vector(4, 5)); assert(v - w == Vector(0, 1)); } ``` ## noSuchMethod() ``` class A { // 除非你覆盖noSuchMethod,否则使用 不存在的成员导致 NoSuchMethodError。 @override void noSuchMethod(Invocation invocation) { print('You tried to use a non-existent member: ' + '${invocation.memberName}'); } } ``` ## 枚举类型 枚举类型(通常称为enumerations 或者 enums)是一种特殊类,用于表示固定数量的常量值。 使用 `enum` 关键字声明枚举类型: ``` enum Color { red, green, blue } // Color.red.index == 0,index 从0 开始 List<Color> colors = Color.values; // 通过values 获取所有枚举值 assert(colors[2] == Color.blue); ``` ## mixins Mixins 是一种在多个类层次结构中 复用类代码的方法。 ``` // 要实现一个 mixin,创建一个扩展 Object 的类,没有构造函数,并且没有调用 super 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'); } } } // 通过 with 关键字 使用 mixins class Maestro extends Person with Musical, Aggressive, Demented { Maestro(String maestroName) { name = maestroName; canConduct = true; } } ``` > [为类添加功能:Mixin](https://www.dartcn.com/guides/language/language-tour#%E4%B8%BA%E7%B1%BB%E6%B7%BB%E5%8A%A0%E5%8A%9F%E8%83%BD-mixin) ## static 对于常见或广泛使用的实用程序和功能,请考虑使用 **顶级函数** 而不是 静态方法。 您可以使用静态方法作为编译时常量。例如,您**可以将静态方法作为参数传递给常量构造函数**。 ``` import 'dart:math'; class Point { static const initialCapacity = 16; 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(Queue.initialCapacity == 16); assert(2.8 < distance && distance < 2.9); print(distance); } ``` # 泛型 Generics 如果查看基本数组类型的API文档 [`List`](https://api.dartlang.org/stable/dart-core/List-class.html),您会看到该类型实际上是List <E>。 <...> 符号 将 List 标记为 generic (或 parameterized 参数化)类型 - 具有形式类型参数的类型。按照惯例,类型变量具有单字母名称,例如 E,T,S,K 和 V。 ```js // 集合字面量 var names = <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 views = Map<int, View>(); //检测运行时 具体类型信息 var names = List<String>(); names.addAll(['Seth', 'Kathy', 'Lars']); print(names is List<String>); // true 检测具体类型信息 ``` ## 限制泛型 通过 `extends` 关键字 限制 泛型 ```js // 通过 extends 关键字 限制 泛型 class Foo<T extends SomeBaseClass> { // Implementation goes here... String toString() => "Instance of 'Foo<$T>'"; } class Extender extends SomeBaseClass {...} // 可以SomeBaseClass或其任何子类作为泛型参数: var someBaseClassFoo = Foo<SomeBaseClass>(); var extenderFoo = Foo<Extender>(); // 也可以不指定泛型参数: var foo = Foo(); print(foo); // Instance of 'Foo<SomeBaseClass>' // 其他情况会导致错误 var foo = Foo<Object>(); ``` ### 泛型方法 最初Dart 只能限制 class;最新语法,通过泛型方法,对函数、方法使用类型参数。 1. 在函数的返回中返回类型(T) 2. 在参数的类型中使用(List<T>) 3. 在局部变量的类型中(T tmp) ```dart T first<T>(List<T> ts) { // 做一些初步工作或错误检查,然后...... T tmp = ts[0]; // 做一些额外的检查或处理...... return tmp; } ``` # 异步支持 Dart库中包含许多返回 [Future](https://api.dartlang.org/stable/dart-async/Future-class.html) 或 [Stream](https://api.dartlang.org/stable/dart-async/Stream-class.html) 对象的函数。这些函数是异步的:它们在进行可能耗时的操作(例如I / O)后返回,而不等待该操作完成。 `async` 和 `await` ,让我们可以编写类似同步的代码。 ## 处理 Futures 1. 使用 `async`和 `await`。` await expression` 的 `expression` 通常会被包裹为一个 `Future` 类型。 2. 使用Future API,如[库浏览中所述](https://www.dartlang.org/guides/libraries/library-tour#future)。 ```dart // await 需要的 定义为 async 的异步方法中,返回一个 Future 对象 Future checkVersion() async { try { version = await lookUpVersion(); // Do something with version } catch (e) { // 可以使用 try catch 进行相应处理 } } // main 函数 定义为一个异步函数 Future main() async { checkVersion(); print('In main: version is ${await lookUpVersion()}'); } ``` ## 异步函数 `Future<void>` 没有有效的返回值时,可以这样定义。 ``` Future<String> lookUpVersion() async => '1.0.0'; ``` ## 处理 Streams `await for` **不能对 UI事件监听器 使用**,因为UI事件可能会持续不断的存在。 1. 使用`async`和异步for循环(`await for`)。 2. 使用Stream API,如[库浏览中所述](https://www.dartlang.org/guides/libraries/library-tour#stream)。 ```dart await for (varOrType identifier in expression) { // Executes each time the stream emits a value. } ``` `expression`的值必须具有 Stream 类型。执行过程如下: 1. 等待直到流发出一个值。 2. 执行for 循环的主体,将变量设置为该发出的值。 3. 重复1 和 2,直到关闭流。 要停止侦听流,**可以使用 `break` 或 `return`语句**,该语句会从for循环中断开 并从流中取消订阅。 要在 `main()` 函数中使用异步for循环,`main()` 的主体必须标记为 ·async·: ```dart Future main() async { // ... await for (var request in requestServer) { handleRequest(request); } // ... } ``` ## 生成器函数 当您需要延迟地生成一个值序列时,请考虑使用生成器函数。Dart内置支持两种生成器函数: * **同步生成器**:返回 [Iterable](https://api.dartlang.org/stable/dart-core/Iterable-class.html) 对象 * **异步生成器**:返回 [Stream](https://api.dartlang.org/stable/dart-async/Stream-class.html) 对象 要实现同步生成器函数,将函数体标记为 `sync*`,并使用 `yield` 语句传递值: ```dart Iterable<int> naturalsTo(int n) sync* { int k = 0; while (k < n) yield k++; } ``` 要实现异步生成器函数,将函数体标记为 `async*`,并使用 `yield` 语句传递值: ```js Stream<int> asynchronousNaturalsTo(int n) async* { int k = 0; while (k < n) yield k++; } ``` 如果您的生成器是递归的,您可以使用 `yield*` 来改进它的性能: ```js Iterable<int> naturalsDownFrom(int n) sync* { if (n > 0) { yield n; yield* naturalsDownFrom(n - 1); //递归调用 } } ``` 有关生成器的更多信息,请参阅文章 [Dart语言异步支持:Phase 2](https://www.dartlang.org/articles/language/beyond-async)。 ## 可调用的类 实现 `call()`方法可以让你的Dart 类像函数一样被调用。 ```js // WannabeFunction 类定义了一个 `call()` 函数 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! ``` 有关类的更多信息,请参见[Dart中的模拟函数](https://www.dartlang.org/articles/language/emulating-functions)。 # 隔离器 大多数计算机,甚至在移动平台上,都有多核cpu。为了利用所有这些核心,开发人员通常使用同时运行的共享内存线程。但是,共享状态并发容易出错并且可能增加代码的复杂度。 不同于线程,所有Dart代码都运行在隔离器内部,而不是线程。每个隔离都有它自己的内存堆,确保任何其他隔离器都不能访问隔离状态。 有关更多信息,请参见 [dart:isolate库文档](https://api.dartlang.org/stable/dart-isolate) 。 ## 类型定义 在 Dart 中,函数是对象,就像字符串和数字是对象一样。`typedef`或 `function-type`为函数提供一个类型别名,你可以在声明字段和返回类型时使用这个名称。当函数类型被分配给变量时,`typedef` 保留类型信息。 ```js typedef Compare = int Function(Object a, Object b); class SortedCollection { Compare compare; SortedCollection(this.compare); } // Initial, broken implementation. int sort(Object a, Object b) => 0; void main() { SortedCollection coll = SortedCollection(sort); assert(coll.compare is Function); assert(coll.compare is Compare); } ``` *注意:目前,`typedefs` 仅限于函数类型。我们期望这种情况会改变。 * 因为 `typedef` 仅仅是别名,所以它们提供了一种检查任何函数类型的方法。例如: ``` 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! } ``` # 元数据 (Metadata ) `@deprecated` 和 `@override` 只有这两个注解在所有Dart 代码中,都是可用的。 ``` class Television { /// _Deprecated:改用 [turnOn] ._ @deprecated void activate() { turnOn(); } /// Turns the TV's power on. void turnOn() {...} } ``` ## 自定义元数据注解 元数据可以出现在library、class、typedef、类型参数、构造函数、factory、函数、字段、参数或变量声明之前,也可以出现在 `import` 或 `export`指令之前。您可以使用反射在运行时 检索元数据。 ``` js // 通过 `library` 指令 表明 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'); } ``` # 注释 * 单行注释 以 `//` 开头 ``` void main() { // TODO: refactor into an AbstractLlamaGreetingFactory? print('Welcome to my Llama farm!'); } ``` * 多行注释 以 `/*` 开头 ``` void main() { /* * This is a lot of work. Consider raising chickens. Llama larry = Llama(); larry.feed(); larry.exercise(); larry.clean(); */ } ``` **文档注释**是以`///`或`/**`开头的多行或单行注释。在连续行上使用///与多行文档注释具有相同的效果。 Dart编译器会忽略所有文本,除非它被括在括号中。使用括号,您可以引用类、方法、字段、顶级变量、函数和参数。括号中的名称在文档化程序元素的词法范围内解析。 ```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的文档生成工具](https://github.com/dart-lang/dartdoc#dartdoc) 。 有关生成文档的示例,请参阅 [Dart API文档](https://api.dartlang.org/stable) 。 有关如何构造注释的建议,请参阅 [Dart文档注释的指南](https://www.dartlang.org/guides/language/effective-dart/documentation) 。 # 结束 本页概述了Dart语言中常用的功能。正在实施更多功能,但我们希望它们不会破坏现有代码。有关更多信息,请参阅[Dart语言规范](https://www.dartlang.org/guides/language/spec)和 [有效Dart](https://www.dartlang.org/guides/language/effective-dart)。 # 示例 ```dart import 'dart:math'; abstract class Shape { num get area; } class Circle implements Shape { final num radius; //Circle(this.radius); num get area => pi * pow(radius, 2); } class Square implements Shape { final num side; Square(this.side); num get area => pow(side, 2); } main() { final circle = Circle(2); final square = Square(2); print(circle.area); print(square.area); } //============================= import 'dart:math'; class Rectangle { int width; int height; Point origin; Rectangle({this.origin = const Point(0, 0), this.width = 0, this.height = 0}); @override String toString() => 'Origin: (${origin.x}, ${origin.y}), width: $width, height: $height'; } main() { print(Rectangle(origin: const Point(10, 20), width: 100, height: 200)); print(Rectangle(origin: const Point(10, 10))); print(Rectangle(width: 200)); print(Rectangle()); } class Bicycle { int cadence; int _speed = 0; //设置 只读变量 int get speed => _speed; //int get speed => _speed int gear; Bicycle(this.cadence, this.gear); //构造函数 // change 这 speed variable void applyBrake(int decrement) { _speed -= decrement; } void speedUp(int increment) { _speed += increment; } // 重写默认方法 @override String toString() => 'Bicycle: $_speed mph'; //带名字类似ES6中的胖箭头函数写法 } // 主入口方法 void main() { var bike = Bicycle(2, 1); //可以缺少 new bike.applyBrake(1); //调用方法 print(bike); } ```