此文章是v1.0+時編寫,年代久遠,小心有毒,謹慎食用!!!
一些重要概念
- 所有的東西都是對象,所有的對象都是類的實例。即使 數字、函數、
null
也都是對象。所有的對象都繼承自Object
類。 - 指定靜態類型表明你的意圖,并使檢查類型檢查成為可能。在 Dart 1.x 指定類型是可選的,然而 Dart 正在朝著完全類型安全的語言發展。
- 在 Dart 1.x 中,強類型 strong mode 是可選的,但在 Dart 2 中,默認就是 strong mode。
- Dart 在運行前解析所有的代碼,可以使用些小技巧,例如:通過使用類型或編譯時常量,來捕捉錯誤或使代碼運行的更快。
- Dart 支持頂級的函數,也支持類或對象的靜態和實例方法。也可以在函數內部嵌套函數或本地函數。
- 同樣,Dart 支持頂級的變量,也支持類或對象的靜態變量和實例變量(也被稱作字段或屬性)。
- Dart沒有
public
、protected
、private
等關鍵字,如果一個標識符以_
開頭則表示私有。 - 標識符以小寫字母或下劃線
_
開頭,后面跟著字符和數字的任意組合。 - 明確區分表達式和語句。
- Dart tools 會報告兩種類型的問題:警告(warnings)和錯誤(errors)。警告僅標志著你的代碼可能不會工作,但并不會阻止程序執行;錯誤可能是編譯時錯誤,也可能是運行時錯誤。編譯時錯誤會阻止程序執行;運行時錯誤會在程序執行時拋出異常。
- Dart 1.x 有兩種運行時模式:生產模式和檢查模式。推薦在開發和 debug 時使用檢查模式,部署到生產模式。生產模式時 Dart 程序默認的運行時模式。
- Dart 2 廢棄了 checked mode 默認 strong mode。
- Dart 2 不再使用 Dartium,改為 dartdevc(DDC)。
- 沒有初始化的變量都會被賦予默認值
null
。 -
final
的值只能被設定一次。const
是一個編譯時的常量,可以通過const
來創建常量值,var c=const[];
,這里c
還是一個變量,只是被賦值了一個常量值,它還是可以賦其它值。實例變量可以是final
,但不能是const
。
關鍵字(Keywords)
abstract | continue | false | new | this |
---|---|---|---|---|
as | default | final | null | throw |
assert | deferred | finally | operator | true |
async | do | for | part | try |
async* | dynamic | get | rethrow | typedef |
await | else | if | return | var |
break | enum | implements | set | void |
case | export | import | static | while |
catch | external | in | super | with |
class | extends | is | switch | yield |
const | factory | library | sync | yield |
內置類型(Built-in types)
- numbers
- strings
- booleans
- lists (also known as arrays)
- maps
- runes (for expressing Unicode characters in a string)
- symbols
is is!
操作符判斷對象是否為指定類型,如num、String等。
as
用來類型斷言。
- num
- int 取值范圍:-2^53 to 2^53
- double
// String -> int
var one = int.parse('1');
// String -> double
var onePointOne = double.parse('1.1');
// int -> String
String oneAsString = 1.toString();
// double -> String 注意括號中要有小數點位數,否則報錯
String piAsString = 3.14159.toStringAsFixed(2);
-
String
- Dart 的String 是 UTF-16 編碼的一個隊列。
-
'...',"..."
表示字符串。 -
'''...''',"""..."""
表示多行字符串。 -
r'...',r"..."
表示“raw”字符串。
-
bool
- Dart 是強 bool 類型檢查,只有 bool 類型的值是
true
才被認為是 true。
- Dart 是強 bool 類型檢查,只有 bool 類型的值是
List
// 使用List的構造函數,也可以添加int參數,表示List固定長度,不能進行添加 刪除操作
var vegetables = new List();
// 或者簡單的用List來賦值
var fruits = ['apples', 'oranges'];
// 添加元素
fruits.add('kiwis');
// 添加多個元素
fruits.addAll(['grapes', 'bananas']);
// 獲取List的長度
assert(fruits.length == 5);
// 獲取第一個元素
fruits.first;
// 獲取元素最后一個元素
fruits.last;
// 利用索引獲取元素
assert(fruits[0] == 'apples');
// 查找某個元素的索引號
assert(fruits.indexOf('apples') == 0);
// 刪除指定位置的元素,返回刪除的元素
fruits.removeAt(index);
// 刪除指定元素,成功返回true,失敗返回false
fruits.remove('apples');
// 刪除最后一個元素,返回刪除的元素
fruits.removeLast();
// 刪除指定范圍元素,含頭不含尾,成功返回null
fruits.removeRange(start,end);
// 刪除指定條件的元素,成功返回null
fruits.removeWhere((item) => item.length >6);
// 刪除所有的元素
fruits.clear();
assert(fruits.length == 0);
// sort()對元素進行排序,傳入一個函數作為參數,return <0表示由小到大, >0表示由大到小
fruits.sort((a, b) => a.compareTo(b));
- Map
// Map的聲明
var hawaiianBeaches = {
'oahu' : ['waikiki', 'kailua', 'waimanalo'],
'big island' : ['wailea bay', 'pololu beach'],
'kauai' : ['hanalei', 'poipu']
};
var searchTerms = new Map();
// 指定鍵值對的參數類型
var nobleGases = new Map<int, String>();
// Map的賦值,中括號中是Key,這里可不是數組
nobleGase[54] = 'dart';
//Map中的鍵值對是唯一的
//同Set不同,第二次輸入的Key如果存在,Value會覆蓋之前的數據
nobleGases[54] = 'xenon';
assert(nobleGases[54] == 'xenon');
// 檢索Map是否含有某Key
assert(nobleGases.containsKey(54));
//刪除某個鍵值對
nobleGases.remove(54);
assert(!nobleGases.containsKey(54));
- Runes
Dart 中 runes 是UTF-32字符集的string 對象。
string = 'Dart';
string.codeUnitAt(0); // 68 返回指定位置index的16位UTF-16編碼單元
string.codeUnits; // [68, 97, 114, 116] 返回一個只讀的List,包含字符串中所有的UTF-16編碼單元。
- Symbol
symbol字面量是編譯時常量,在標識符前面加#
。如果是動態確定,則使用Symbol構造函數,通過new
來實例化。
函數(Functions)
所有的函數都會有返回值。如果沒有指定函數返回值,則默認的返回值是null
。沒有返回值的函數,系統會在最后添加隱式的return
語句。
函數可以有兩種類型的參數:
- 必須的——必須的參數放在參數列表的前面。
- 可選的——可選的參數跟在必須的參數后面。
可選的參數
可以通過名字或位置指定可選參數,但是兩個不能同時存在。
- 命名可選參數使用
{}
,默認值使用冒號:
,調用時需指明參數名,與順序無關。 - 位置可選參數使用
[]
,默認值使用等號=
,調用時參數按順序賦值。
操作符(Operators)
表中,操作符的優先級依次降低。
描述 | 操作符 | |
---|---|---|
一元后置操作符 | expr++ expr-- () [] . ?. | |
一元前置操作符 | -expr !expr ~expr ++expr --expr | |
乘除 | * / % ~/ | |
加減 | + - | |
位移 | << >> | |
按位與 | & | |
按位異或 | ^ | |
按位或 | ||
關系和類型判斷 | >= > <= < as is is! | |
等 | == != | |
邏輯與 | && | |
邏輯或 | ||
若為null | ?? | |
條件表達式 | expr1 ? expr2 : expr3 | |
級聯 | .. | |
賦值 | = *= /= ~/= %= += -= <<= >>= &= ^= | = ??= |
流程控制語句(Control flow statements)
- if...else
- for
- while do-whild
- break continue
- switch...case
- assert(僅在checked模式有效)
異常(Exceptions)
throw
- 拋出固定類型的異常:
throw new FormatException('Expected at least 1 section');
- 拋出任意類型的異常:
throw 'Out of llamas!';
- 因為拋出異常屬于表達式,可以將throw語句放在=>語句中,或者其它可以出現表達式的地方:
distanceTo(Point other) =>
throw new UnimplementedError();
catch
將可能出現異常的代碼放置到try
語句中,可以通過 on
語句來指定需要捕獲的異常類型,使用catch
來處理異常。
try {
breedMoreLlamas();
} on OutOfLlamasException {
// A specific exception
buyMoreLlamas();
} on Exception catch (e) {
// Anything else that is an exception
print('Unknown exception: $e');
} catch (e, s) {
print('Exception details:\n $e');
print('Stack trace:\n $s');
}
可以向catch()
傳遞1個或2個參數。第一個參數表示:捕獲的異常的具體信息,第二個參數表示:異常的堆棧跟蹤(stack trace)。
rethrow
rethrow
語句用來處理一個異常,同時希望這個異常能夠被其它調用的部分使用。
final foo = '';
void misbehave() {
try {
foo = "1";
} catch (e) {
print('2');
rethrow;// 如果不重新拋出異常,main函數中的catch語句執行不到
}
}
void main() {
try {
misbehave();
} catch (e) {
print('3');
}
}
finally
Dart 的finally
用來執行那些無論異常是否發生都執行的操作。
final foo = '';
void misbehave() {
try {
foo = "1";
} catch (e) {
print('2');
}
}
void main() {
try {
misbehave();
} catch (e) {
print('3');
} finally {
print('4'); // 即使沒有rethrow最終都會執行到
}
}
類(Classes)
Dart 是一種面向對象的語言,并且支持基于mixin的繼承方式:一個類可以繼承自多個父類。
使用new
語句來構造一個類。構造函數的名字可能是ClassName,也可以是ClassName.identifier
。例如:
var jsonData = JSON.decode('{"x":1, "y":2}');
// Create a Point using Point().
var p1 = new Point(2, 2);
// Create a Point using Point.fromJson().
var p2 = new Point.fromJson(jsonData);
- 使用
.
來調用實例的變量或者方法。 - 使用
?.
來避免左邊操作數為null
引發異常。 - 使用
const
替代new
來創建編譯時的常量構造函數。 - 兩個使用
const
構建的同一個構造函數,實例相等。 - 獲取對象的運行時類型使用:
o.runtimeType
。
實例化變量
在類定義中,所有沒有初始化的變量都會被初始化為null
。
所有實例變量會生成一個隱式的getter
方法,不是final
或const
的實例變量也會生成一個隱式的setter
方法。
構造函數
class Point {
num x;
num y;
Point(num x, num y) {
// 這不是最好的方式.
this.x = x; // this關鍵字指向當前類的實例
this.y = y;
}
}
class Point {
num x;
num y;
// 推薦方式
Point(this.x, this.y);
}
默認構造函數
沒有聲明構造函數,dart會提供默認構造函數。默認構造函數沒有參數,并且調用父類的無參數構造函數。
構造函數不能被繼承
子類不會繼承父類的構造函數。如果不顯式提供子類的構造函數,系統就提供默認的構造函數。
命名構造函數
class Point {
num x;
num y;
Point(this.x, this.y);
// 命名構造函數
Point.fromJson(Map json) {
x = json['x'];
y = json['y'];
}
}
使用命名構造函數可以實現一個類多個構造函數。構造函數不能被繼承,父類中的命名構造函數不能被子類繼承。如果想要子類也擁有一個父類一樣名字的構造函數,必須在子類實現這個構造函數。
調用父類的非默認構造函數
默認情況下,子類調用父類的無參數的非命名構造函數。父類的構造函數會在子類的構造函數體之前(大括號前)調用。如果也使用了初始化列表 ,則會先執行初始化列表 中的內容,即下面的順序:
- 初始化列表
- 父類無參數構造函數
- 主類無參數構造函數
如果父類不顯式提供無參的非命名構造函數,在子類中必須手動調用父類的一個構造函數。在子類構造函數名后,大括號{
前,使用super.
調用父類的構造函數,中間使用:
分割。
父類構造函數參數不能訪問this
,例如,參數可以調用靜態方法但不能調用實例方法。
class Person {
String firstName;
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
// 父類沒有無參數的非命名構造函數,必須手動調用一個構造函數 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';
}
因為父類構造函數的參數是在被調用之前確認值的,所以參數可以是一個表達式,像一個函數的調用。
class Employee extends Person {
// ...
Employee() : super.fromJson(findDefaultData()); // 一個函數調用作為參數
}
當在構造函數初始化列表中使用super()
時,要把它放在最后。
View(Style style, List children)
: _children = children,
super(style) {}
初始化列表
除了調用父類的構造函數,也可以通過初始化列表 在子類的構造函數體前(大括號前)來初始化實例的變量值,使用逗號,
分隔。如下所示:
一個初始化器的右邊不能訪問this
。
class Point {
num x;
num y;
Point(this.x, this.y);
// 在構造函數體前 初始化列表 設置實例變量
Point.fromJson(Map jsonMap)
: x = jsonMap['x'],
y = jsonMap['y'] {
print('In Point.fromJson(): ($x, $y)');
}
}
重定向構造函數
有時候構造函數的目的只是重定向到該類的另一個構造函數。重定向構造函數沒有函數體,使用冒號:
分隔。
class Point {
num x;
num y;
// 主構造函數
Point(this.x, this.y) {
print("Point($x, $y)");
}
// 重定向構造函數,指向主構造函數,函數體為空
Point.alongXAxis(num x) : this(x, 0);
}
void main() {
var p1 = new Point(1, 2);
var p2 = new Point.alongXAxis(4);
}
常量構造函數
如果類的對象不會發生變化,可以構造一個編譯時的常量構造函數。定義格式如下:
- 定義所有的實例變量是
final
。 - 使用
const
聲明構造函數。
class ImmutablePoint {
final num x;
final num y;
const ImmutablePoint(this.x, this.y);
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
}
工廠構造函數
當實例化了一個構造函數后,不想每次都創建該類的一個新的實例的時候使用factory
關鍵字,定義工廠構造函數,從緩存中返回一個實例,或返回一個子類型的實例。
工廠構造函數不能訪問this
class Logger {
final String name;
bool mute = false;
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);
}
}
}
main() {
var p1 = new Logger("1");
p1.log("2");
var p2 = new Logger('22');
p2.log('3');
var p3 = new Logger('1');// 相同對象直接訪問緩存
}
方法
實例方法
實例方法可以訪問實例變量和this
。
Getters and setters
get()
和set()
方法是Dart 語言提供的專門用來讀取和寫入對象的屬性的方法。每一個類的實例變量都有一個隱式的getter
和可能的setter
(如果字段為final
或const
,只有getter
)。
像++
之類的操作符,無論是否明確定義了getter,都按預期的方式工作。為了避免意想不到的副作用,操作符調用getter一次,將其值保存在臨時變量中。
class Rectangle {
num left;
num top;
num width;
num height;
Rectangle(this.left, this.top, this.width, this.height);
// Define two calculated properties: right and 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;
}
main() {
var rect = new Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;
assert(rect.left == -8);
}
抽象方法
抽象方法,只有函數聲明,沒有函數體,以分號;
結尾,作為接口在其他類中實現。調用抽象方法會導致運行時錯誤。如果在不是抽象類中定義抽象方法會引發warning
,但不會阻止實例化。
abstract class Doer {
...
void doSomething(); // 定義抽象方法
}
class EffectiveDoer extends Doer {
void doSomething() {
// ...Provide an implementation, so the method is not abstract here...
}
}
可重寫的運算符
下面是可重寫的運算符,在運算符前面使用operator
關鍵字。
如果重寫==
,需要重寫Object
的hashCode
getter。
< | + | { | [] |
---|---|---|---|
> | / | ^ | []= |
<= | ~/ | & | ~ |
>= | * | << | == |
– | % | >> |
抽象類
使用abstract
關鍵字定義一個抽象類,抽象類不能實例化。抽象類通常用來定義接口。
假如需要將抽象類實例化,需要定義一個factory constructor
。
抽象類通常會包含一些抽象的方法。
abstract class AbstractContainer { // 抽象類
// ....
void updateChildren(); // 抽象方法
}
隱式接口
每一個類都隱式的定義一個接口,這個接口包含了這個類的所有實例成員和它實現的所有接口。
一個類可以實現一個或多個(用,
隔開)接口,通過implements
關鍵字。
class Person {
final _name;
Person(this._name);
String greet(who) => 'hello,$who,i am $_name';
}
class Imposter implements Person {
final _name = '';
String greet(who) => 'hi $who.do you know who i am.';
}
greetBob(Person p) => p.greet('bob');
main(List<String> args) {
print(greetBob(new Person('lili')));
print(greetBob(new Imposter()));
}
繼承
使用extends
來創造子類,使用super
來指向父類。
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ...
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ...
}
子類可以重載實例方法,getter和setter。使用@override
注解重載方法。
class A {
@override
void noSuchMethod(Invocation mirror) {
print('You tried to use a non-existent member:' +
'${mirror.memberName}');
}
}
如果使用noSuchMethod()
來實現每一個可能的getter,setter和一個或多個類型的方法,可以使用@proxy
注解來避免警告:
@proxy
class A {
void noSuchMethod(Invocation mirror) {
// ...
}
}
如果知道編譯時類型,可以不使用@proxy
,只需要使類實現這些類型。
class A implements SomeClass, SomeOtherClass {
void noSuchMethod(Invocation mirror) {
// ...
}
}
枚舉類型
枚舉類型是一種特殊的類,通常用來表示一組固定數字的常量值。
使用enum
關鍵字聲明枚舉類型。
enum Color {
red,
green,
blue
}
每個枚舉類型都有一個index
的getter,返回以0開始的位置索引,每次加1。
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
使用values
關鍵字獲取枚舉的所有值,返回一個列表。
print(Color.values); // [Color.red, Color.green, Color.blue]
在switch語句中使用枚舉,必須在case語句中判斷所有的枚舉,否則會獲得警告。
enum Color {
red,
green,
blue
}
// ...
Color aColor = Color.blue;
switch (aColor) {
case Color.red:
print('Red as roses!');
break;
case Color.green:
print('Green as grass!');
break;
default: //必須完成所有判斷,否則會引發warning
print(aColor); // 'Color.blue'
}
枚舉類型有以下限制:
- 不能繼承,mixin,或實現一個枚舉。
- 不能顯式的實例化一個枚舉。
minxins
mixins是一種在多個類層次結構中,重用一個類的代碼的方法。使用with
關鍵字后跟多個mixin名。
class Musician extends Performer with Musical {
// ...
}
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
class變量和方法
使用static
關鍵字來實現類范圍內變量和方法。
- 靜態變量
靜態變量在使用時初始化。
class Color {
static const red =
const Color('red'); // A constant static variable.
final String name; // An instance variable.
const Color(this.name); // A constant constructor.
}
main() {
assert(Color.red.name == 'red');
}
- 靜態方法
靜態方法不能被實例調用,不能訪問this
,只能被類名調用。
import 'dart:math';
class Point {
num x;
num 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);
}
}
main() {
var a = new Point(2, 2);
var b = new Point(4, 4);
var distance = Point.distanceBetween(a, b);
print(distance); // 2.8284271247461903
}
對于通用或廣泛使用的工具和功能,應該使用頂級函數,而不是靜態方法。
可以使用靜態方法作為編譯時常量。例如,可以給一個常量構造函數,傳遞一個靜態方法作為參數。
泛型(Generics)
使用<...>
的方式來定義泛型。
為什么使用泛型
- 雖然Dart 語言中類型是可選的,但是明確的指明使用的是泛型,會讓代碼更好理解。
var names = new List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
// ...
names.add(42); // Fails in checked mode (succeeds in production mode).
- 使用泛型減少代碼重復。
代碼片段一:
abstract class ObjectCache {
Object getByKey(String key);
setByKey(String key, Object value);
}
代碼片段二:
abstract class StringCache {
String getByKey(String key);
setByKey(String key, String value);
}
以上的兩段代碼可以使用泛型簡化如下:
abstract class Cache<T> {
T getByKey(String key);
setByKey(String key, T value);
}
用于集合類型(Using collection literals)
泛型用于List
和 Map
類型參數化。
- List:
<type>
- Map:
<keyType, valueType>
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 names = new List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
var nameSet = new Set<String>.from(names);
var views = new Map<int, View>();
泛型集合及它們所包含的類型
dart的泛型類型是具體的,在運行時包含它們的類型信息。
var names = new List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
然而,is
表達式檢查的是集合的類型,而不是里面的對象。在生產模式下,一個List<Stirng>
可能有很多不是string
的條目在里面。解決辦法是檢查每一項,或者包裹在一個異常處理程序中。
限制參數化類型
當實現一個泛型時,如果需要限制它參數的類型,可以使用extends
關鍵字。
// T 必須是SomeBaseClass或它的后代
class Foo<T extends SomeBaseClass> {...}
class Extender extends SomeBaseClass {...}
void main() {
// It's OK to use SomeBaseClass or any of its subclasses inside <>.
var someBaseClassFoo = new Foo<SomeBaseClass>();
var extenderFoo = new Foo<Extender>();
// It's also OK to use no <> at all.
var foo = new Foo();
// Specifying any non-SomeBaseClass type results in a warning and, in
// checked mode, a runtime error.
// var objectFoo = new Foo<Object>();
}
庫和可見性
使用import
和 library
指令可以方便的創建一個模塊或分享代碼。一個Dart 庫不僅能夠提供相應的API,還可以包含一些以_
開頭的私有變量僅在庫內部可見。
每一個Dart 應用都是一個庫,即使它沒有使用library
指令。庫可以方便是使用各種類型的包。
使用庫(Libraries and visibility)
使用import
指定怎樣的命名空間,從一個庫引用另一個庫。
import
唯一要求的參數是指定庫的URI。
- dart內置庫,URI組合
dart:
- 其他庫,使用文件路徑或
package:
組合。package:
組合式通過包管理工具提供的。
import 'dart:html';
import 'package:mylib/mylib.dart';
指定一個庫的前綴
如果導入的庫擁有相互沖突的名字,使用as
為其中一個或幾個指定不一樣的前綴。
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// ...
Element element1 = new Element(); // Uses Element from lib1.
lib2.Element element2 = new lib2.Element(); // Uses Element from lib2.
導入庫的一部分
如果只需要使用庫的一部分內容,使用show
或hide
有選擇的導入。
// 僅導入foo.
import 'package:lib1/lib1.dart' show foo;
// 除了foo都導入
import 'package:lib2/lib2.dart' hide foo;
延遲加載庫
延遲加載,也叫懶加載,允許應用程序按需加載庫。使用延遲加載的場景:
- 減少程序初始化啟動時間。
- 執行A/B測試——嘗試替換一個算法的實現。
- 加載很少用的功能,比如可選的屏幕和對話框。
要延遲加載一個庫,首先必須使用deferred as
導入它。
import 'package:deferred/hello.dart' deferred as hello;
當需要使用庫的時候,使用庫名調用loadLibrary()
。
greet() async {
// 使用await關鍵字暫停執行,直到庫加載
await hello.loadLibrary();
hello.printGreeting();
}
可以在代碼中多次調用loadLibrary()
方法。但是實際上它只會被執行一次。
使用延遲加載的注意事項:
- 延遲加載的內容只有在加載后才存在。
- Dart 隱式的將
deferred as
改為了deferred as namespace
。loadLibrary()
返回值是Future
。
異步支持(Asynchrony support)
使用async
函數和await
表達式實現異步操作。
當需要使用一個從Future
返回的值時,有兩個選擇:
- 使用
async
和await
。 - 使用
Future API
。
當需要從一個Stream
獲取值時,有兩個選擇:
- 使用
async
和異步的循環(await for
)。 - 使用
Stream API
。
代碼使用了async
和await
就是異步的,雖然看起來像同步代碼。
await lookUpVersion()
要使用await
,代碼必須在函數后面標記為async
:
checkVersion() async {
var version = await lookUpVersion();
if (version == expectedVersion) {
// Do something.
} else {
// Do something else.
}
}
可以使用try,catch和finally
配合await
處理錯誤。
try {
server = await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 4044);
} catch (e) {
// 無法綁定到端口時......
}
聲明異步函數
異步函數是一個被async
標記的函數。
雖然異步的函數中可能執行耗時的操作,但是函數本身在調用后將會立即返回,即使函數體一條語句也沒執行。
checkVersion() async {
// ...
}
lookUpVersion() async => /* ... */;
給函數添加async
關鍵字將使函數返回一個Future
類型。
// 修改前是同步的
String lookUpVersionSync() => '1.0.0';
// 修改后 是異步的 函數體不需要使用Future API
// dart會在必要的時候創建Future對象
Future<String> lookUpVersion() async => '1.0.0';
在 Future 中使用 await 表達式
await expression
可以在異步函數中多次使用await
var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
在await
表達式中,表達式通常是一個Future
。如果表達式不是Future
類型,它將自動被包裝為Future
類型。await expression
的返回值是一個對象。await表達式使執行暫停,直到對象可用。
如果await
不工作,確保await
處于async
函數中。即使是在main函數中,也要標記為async
。
main() async {
checkVersion();
// 這里使用了 await
print('In main: version is ${await lookUpVersion()}');
}
在Stream中使用異步循環
// expression的值必須是Stram類型
await for (variable declaration in expression) {
// Executes each time the stream emits a value.
}
異步循環的執行流程如下:
- 等待 stream 發出數據。
- 執行循環體,并將變量的值設置為發出的數據。
- 重復1.,2.直到stream 對象被關閉。
結束監聽stram 可以使用break
或returen
語句,跳出for
循環,取消訂閱stream。
如果異步循環不工作,確保是在一個async
函數中,如果使用異步循環在main()
函數中,也要確保main()
函數被標記為async
。
main() async {
...
await for (var request in requestServer) {
handleRequest(request);
}
...
}
可調用類(Callable classes)
Dart 語言中為了能夠讓類像函數一樣能夠被調用,可以實現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!
print(wf.runtimeType); // WannabeFunction
print(out.runtimeType); // String
print(wf is Function); // true
}
隔離(Isolates)
Dart 代碼都運行在獨立的隔離空間中,每個隔離空間都有自己的內存堆棧,確保每個隔離空間的狀態不會被其它的隔離空間訪問。
類型定義(Typedefs)
typedef
關鍵字,用來聲明一種類型,當一個函數類型分配給一個變量時,保留類型信息。
// 聲明一種 Compare類型
typedef int Compare(int a, int b);
int sort(int a, int b) => a - b;
main() {
assert(sort is Compare); // True!
}
當前 typedef 僅用于函數類型。
元數據(Metadata)
元數據是以@
開始的修飾符。在@
后面接著編譯時的常量或調用一個常量構造函數。
所有dart代碼中可用的三個注解:
- @deprecated 被棄用的
- @override 重載
- @proxy 代理
定義自己的元數據
通過library
來定義一個庫,在庫中定義一個相同名字的class
,然后在類中定義const
構造方法。
// 定義
library todo;
class todo {
final String who;
final String what;
const todo(this.who, this.what);
}
// 使用
import 'todo.dart';
@todo('seth', 'make this do something')
void doSomething() {
print('do something');
}
元數據可以修飾library
(庫), class
(類), typedef
(類型定義), type parameter
(類型參數), constructor
(構造函數), factory
(工廠函數), function
(函數), field
(作用域), parameter
(參數), 或者 variable declaration
(變量聲明)。
可以使用reflection
反射,在運行時檢查元數據。
注釋(Comments)
- 單行注釋:
/
- 多行注釋:
/*...*/
- 文檔注釋:
/**...*/
或///
使用[]
引用參考生成API文檔鏈接
class Llama {
String name;// 這是單行注釋
void feed(Food food) {
/*
這是多行注釋
/
}
/**
這里是文檔注釋
*/
/// Exercises your llama with an [activity] for
/// [timeLimit] minutes.
void exercise(Activity activity, int timeLimit) {
// ...
}
}