[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)

帶有字樣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);
}
```