此文章是v1.0+時(shí)編寫,年代久遠(yuǎn),小心有毒,謹(jǐn)慎食用!!!
一些重要概念
- 所有的東西都是對象,所有的對象都是類的實(shí)例。即使 數(shù)字、函數(shù)、
null
也都是對象。所有的對象都繼承自Object
類。 - 指定靜態(tài)類型表明你的意圖,并使檢查類型檢查成為可能。在 Dart 1.x 指定類型是可選的,然而 Dart 正在朝著完全類型安全的語言發(fā)展。
- 在 Dart 1.x 中,強(qiáng)類型 strong mode 是可選的,但在 Dart 2 中,默認(rèn)就是 strong mode。
- Dart 在運(yùn)行前解析所有的代碼,可以使用些小技巧,例如:通過使用類型或編譯時(shí)常量,來捕捉錯(cuò)誤或使代碼運(yùn)行的更快。
- Dart 支持頂級的函數(shù),也支持類或?qū)ο蟮撵o態(tài)和實(shí)例方法。也可以在函數(shù)內(nèi)部嵌套函數(shù)或本地函數(shù)。
- 同樣,Dart 支持頂級的變量,也支持類或?qū)ο蟮撵o態(tài)變量和實(shí)例變量(也被稱作字段或?qū)傩?。
- Dart沒有
public
、protected
、private
等關(guān)鍵字,如果一個(gè)標(biāo)識符以_
開頭則表示私有。 - 標(biāo)識符以小寫字母或下劃線
_
開頭,后面跟著字符和數(shù)字的任意組合。 - 明確區(qū)分表達(dá)式和語句。
- Dart tools 會(huì)報(bào)告兩種類型的問題:警告(warnings)和錯(cuò)誤(errors)。警告僅標(biāo)志著你的代碼可能不會(huì)工作,但并不會(huì)阻止程序執(zhí)行;錯(cuò)誤可能是編譯時(shí)錯(cuò)誤,也可能是運(yùn)行時(shí)錯(cuò)誤。編譯時(shí)錯(cuò)誤會(huì)阻止程序執(zhí)行;運(yùn)行時(shí)錯(cuò)誤會(huì)在程序執(zhí)行時(shí)拋出異常。
- Dart 1.x 有兩種運(yùn)行時(shí)模式:生產(chǎn)模式和檢查模式。推薦在開發(fā)和 debug 時(shí)使用檢查模式,部署到生產(chǎn)模式。生產(chǎn)模式時(shí) Dart 程序默認(rèn)的運(yùn)行時(shí)模式。
- Dart 2 廢棄了 checked mode 默認(rèn) strong mode。
- Dart 2 不再使用 Dartium,改為 dartdevc(DDC)。
- 沒有初始化的變量都會(huì)被賦予默認(rèn)值
null
。 -
final
的值只能被設(shè)定一次。const
是一個(gè)編譯時(shí)的常量,可以通過const
來創(chuàng)建常量值,var c=const[];
,這里c
還是一個(gè)變量,只是被賦值了一個(gè)常量值,它還是可以賦其它值。實(shí)例變量可以是final
,但不能是const
。
關(guān)鍵字(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 |
內(nèi)置類型(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 注意括號中要有小數(shù)點(diǎn)位數(shù),否則報(bào)錯(cuò)
String piAsString = 3.14159.toStringAsFixed(2);
-
String
- Dart 的String 是 UTF-16 編碼的一個(gè)隊(duì)列。
-
'...',"..."
表示字符串。 -
'''...''',"""..."""
表示多行字符串。 -
r'...',r"..."
表示“raw”字符串。
-
bool
- Dart 是強(qiáng) bool 類型檢查,只有 bool 類型的值是
true
才被認(rèn)為是 true。
- Dart 是強(qiáng) bool 類型檢查,只有 bool 類型的值是
List
// 使用List的構(gòu)造函數(shù),也可以添加int參數(shù),表示List固定長度,不能進(jìn)行添加 刪除操作
var vegetables = new List();
// 或者簡單的用List來賦值
var fruits = ['apples', 'oranges'];
// 添加元素
fruits.add('kiwis');
// 添加多個(gè)元素
fruits.addAll(['grapes', 'bananas']);
// 獲取List的長度
assert(fruits.length == 5);
// 獲取第一個(gè)元素
fruits.first;
// 獲取元素最后一個(gè)元素
fruits.last;
// 利用索引獲取元素
assert(fruits[0] == 'apples');
// 查找某個(gè)元素的索引號
assert(fruits.indexOf('apples') == 0);
// 刪除指定位置的元素,返回刪除的元素
fruits.removeAt(index);
// 刪除指定元素,成功返回true,失敗返回false
fruits.remove('apples');
// 刪除最后一個(gè)元素,返回刪除的元素
fruits.removeLast();
// 刪除指定范圍元素,含頭不含尾,成功返回null
fruits.removeRange(start,end);
// 刪除指定條件的元素,成功返回null
fruits.removeWhere((item) => item.length >6);
// 刪除所有的元素
fruits.clear();
assert(fruits.length == 0);
// sort()對元素進(jìn)行排序,傳入一個(gè)函數(shù)作為參數(shù),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();
// 指定鍵值對的參數(shù)類型
var nobleGases = new Map<int, String>();
// Map的賦值,中括號中是Key,這里可不是數(shù)組
nobleGase[54] = 'dart';
//Map中的鍵值對是唯一的
//同Set不同,第二次輸入的Key如果存在,Value會(huì)覆蓋之前的數(shù)據(jù)
nobleGases[54] = 'xenon';
assert(nobleGases[54] == 'xenon');
// 檢索Map是否含有某Key
assert(nobleGases.containsKey(54));
//刪除某個(gè)鍵值對
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] 返回一個(gè)只讀的List,包含字符串中所有的UTF-16編碼單元。
- Symbol
symbol字面量是編譯時(shí)常量,在標(biāo)識符前面加#
。如果是動(dòng)態(tài)確定,則使用Symbol構(gòu)造函數(shù),通過new
來實(shí)例化。
函數(shù)(Functions)
所有的函數(shù)都會(huì)有返回值。如果沒有指定函數(shù)返回值,則默認(rèn)的返回值是null
。沒有返回值的函數(shù),系統(tǒng)會(huì)在最后添加隱式的return
語句。
函數(shù)可以有兩種類型的參數(shù):
- 必須的——必須的參數(shù)放在參數(shù)列表的前面。
- 可選的——可選的參數(shù)跟在必須的參數(shù)后面。
可選的參數(shù)
可以通過名字或位置指定可選參數(shù),但是兩個(gè)不能同時(shí)存在。
- 命名可選參數(shù)使用
{}
,默認(rèn)值使用冒號:
,調(diào)用時(shí)需指明參數(shù)名,與順序無關(guān)。 - 位置可選參數(shù)使用
[]
,默認(rèn)值使用等號=
,調(diào)用時(shí)參數(shù)按順序賦值。
操作符(Operators)
表中,操作符的優(yōu)先級依次降低。
描述 | 操作符 | |
---|---|---|
一元后置操作符 | expr++ expr-- () [] . ?. | |
一元前置操作符 | -expr !expr ~expr ++expr --expr | |
乘除 | * / % ~/ | |
加減 | + - | |
位移 | << >> | |
按位與 | & | |
按位異或 | ^ | |
按位或 | ||
關(guān)系和類型判斷 | >= > <= < as is is! | |
等 | == != | |
邏輯與 | && | |
邏輯或 | ||
若為null | ?? | |
條件表達(dá)式 | expr1 ? expr2 : expr3 | |
級聯(lián) | .. | |
賦值 | = *= /= ~/= %= += -= <<= >>= &= ^= | = ??= |
流程控制語句(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!';
- 因?yàn)閽伋霎惓儆诒磉_(dá)式,可以將throw語句放在=>語句中,或者其它可以出現(xiàn)表達(dá)式的地方:
distanceTo(Point other) =>
throw new UnimplementedError();
catch
將可能出現(xiàn)異常的代碼放置到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個(gè)或2個(gè)參數(shù)。第一個(gè)參數(shù)表示:捕獲的異常的具體信息,第二個(gè)參數(shù)表示:異常的堆棧跟蹤(stack trace)。
rethrow
rethrow
語句用來處理一個(gè)異常,同時(shí)希望這個(gè)異常能夠被其它調(diào)用的部分使用。
final foo = '';
void misbehave() {
try {
foo = "1";
} catch (e) {
print('2');
rethrow;// 如果不重新拋出異常,main函數(shù)中的catch語句執(zhí)行不到
}
}
void main() {
try {
misbehave();
} catch (e) {
print('3');
}
}
finally
Dart 的finally
用來執(zhí)行那些無論異常是否發(fā)生都執(zhí)行的操作。
final foo = '';
void misbehave() {
try {
foo = "1";
} catch (e) {
print('2');
}
}
void main() {
try {
misbehave();
} catch (e) {
print('3');
} finally {
print('4'); // 即使沒有rethrow最終都會(huì)執(zhí)行到
}
}
類(Classes)
Dart 是一種面向?qū)ο蟮恼Z言,并且支持基于mixin的繼承方式:一個(gè)類可以繼承自多個(gè)父類。
使用new
語句來構(gòu)造一個(gè)類。構(gòu)造函數(shù)的名字可能是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);
- 使用
.
來調(diào)用實(shí)例的變量或者方法。 - 使用
?.
來避免左邊操作數(shù)為null
引發(fā)異常。 - 使用
const
替代new
來創(chuàng)建編譯時(shí)的常量構(gòu)造函數(shù)。 - 兩個(gè)使用
const
構(gòu)建的同一個(gè)構(gòu)造函數(shù),實(shí)例相等。 - 獲取對象的運(yùn)行時(shí)類型使用:
o.runtimeType
。
實(shí)例化變量
在類定義中,所有沒有初始化的變量都會(huì)被初始化為null
。
所有實(shí)例變量會(huì)生成一個(gè)隱式的getter
方法,不是final
或const
的實(shí)例變量也會(huì)生成一個(gè)隱式的setter
方法。
構(gòu)造函數(shù)
class Point {
num x;
num y;
Point(num x, num y) {
// 這不是最好的方式.
this.x = x; // this關(guān)鍵字指向當(dāng)前類的實(shí)例
this.y = y;
}
}
class Point {
num x;
num y;
// 推薦方式
Point(this.x, this.y);
}
默認(rèn)構(gòu)造函數(shù)
沒有聲明構(gòu)造函數(shù),dart會(huì)提供默認(rèn)構(gòu)造函數(shù)。默認(rèn)構(gòu)造函數(shù)沒有參數(shù),并且調(diào)用父類的無參數(shù)構(gòu)造函數(shù)。
構(gòu)造函數(shù)不能被繼承
子類不會(huì)繼承父類的構(gòu)造函數(shù)。如果不顯式提供子類的構(gòu)造函數(shù),系統(tǒng)就提供默認(rèn)的構(gòu)造函數(shù)。
命名構(gòu)造函數(shù)
class Point {
num x;
num y;
Point(this.x, this.y);
// 命名構(gòu)造函數(shù)
Point.fromJson(Map json) {
x = json['x'];
y = json['y'];
}
}
使用命名構(gòu)造函數(shù)可以實(shí)現(xiàn)一個(gè)類多個(gè)構(gòu)造函數(shù)。構(gòu)造函數(shù)不能被繼承,父類中的命名構(gòu)造函數(shù)不能被子類繼承。如果想要子類也擁有一個(gè)父類一樣名字的構(gòu)造函數(shù),必須在子類實(shí)現(xiàn)這個(gè)構(gòu)造函數(shù)。
調(diào)用父類的非默認(rèn)構(gòu)造函數(shù)
默認(rèn)情況下,子類調(diào)用父類的無參數(shù)的非命名構(gòu)造函數(shù)。父類的構(gòu)造函數(shù)會(huì)在子類的構(gòu)造函數(shù)體之前(大括號前)調(diào)用。如果也使用了初始化列表 ,則會(huì)先執(zhí)行初始化列表 中的內(nèi)容,即下面的順序:
- 初始化列表
- 父類無參數(shù)構(gòu)造函數(shù)
- 主類無參數(shù)構(gòu)造函數(shù)
如果父類不顯式提供無參的非命名構(gòu)造函數(shù),在子類中必須手動(dòng)調(diào)用父類的一個(gè)構(gòu)造函數(shù)。在子類構(gòu)造函數(shù)名后,大括號{
前,使用super.
調(diào)用父類的構(gòu)造函數(shù),中間使用:
分割。
父類構(gòu)造函數(shù)參數(shù)不能訪問this
,例如,參數(shù)可以調(diào)用靜態(tài)方法但不能調(diào)用實(shí)例方法。
class Person {
String firstName;
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
// 父類沒有無參數(shù)的非命名構(gòu)造函數(shù),必須手動(dòng)調(diào)用一個(gè)構(gòu)造函數(shù) 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';
}
因?yàn)楦割悩?gòu)造函數(shù)的參數(shù)是在被調(diào)用之前確認(rèn)值的,所以參數(shù)可以是一個(gè)表達(dá)式,像一個(gè)函數(shù)的調(diào)用。
class Employee extends Person {
// ...
Employee() : super.fromJson(findDefaultData()); // 一個(gè)函數(shù)調(diào)用作為參數(shù)
}
當(dāng)在構(gòu)造函數(shù)初始化列表中使用super()
時(shí),要把它放在最后。
View(Style style, List children)
: _children = children,
super(style) {}
初始化列表
除了調(diào)用父類的構(gòu)造函數(shù),也可以通過初始化列表 在子類的構(gòu)造函數(shù)體前(大括號前)來初始化實(shí)例的變量值,使用逗號,
分隔。如下所示:
一個(gè)初始化器的右邊不能訪問this
。
class Point {
num x;
num y;
Point(this.x, this.y);
// 在構(gòu)造函數(shù)體前 初始化列表 設(shè)置實(shí)例變量
Point.fromJson(Map jsonMap)
: x = jsonMap['x'],
y = jsonMap['y'] {
print('In Point.fromJson(): ($x, $y)');
}
}
重定向構(gòu)造函數(shù)
有時(shí)候構(gòu)造函數(shù)的目的只是重定向到該類的另一個(gè)構(gòu)造函數(shù)。重定向構(gòu)造函數(shù)沒有函數(shù)體,使用冒號:
分隔。
class Point {
num x;
num y;
// 主構(gòu)造函數(shù)
Point(this.x, this.y) {
print("Point($x, $y)");
}
// 重定向構(gòu)造函數(shù),指向主構(gòu)造函數(shù),函數(shù)體為空
Point.alongXAxis(num x) : this(x, 0);
}
void main() {
var p1 = new Point(1, 2);
var p2 = new Point.alongXAxis(4);
}
常量構(gòu)造函數(shù)
如果類的對象不會(huì)發(fā)生變化,可以構(gòu)造一個(gè)編譯時(shí)的常量構(gòu)造函數(shù)。定義格式如下:
- 定義所有的實(shí)例變量是
final
。 - 使用
const
聲明構(gòu)造函數(shù)。
class ImmutablePoint {
final num x;
final num y;
const ImmutablePoint(this.x, this.y);
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
}
工廠構(gòu)造函數(shù)
當(dāng)實(shí)例化了一個(gè)構(gòu)造函數(shù)后,不想每次都創(chuàng)建該類的一個(gè)新的實(shí)例的時(shí)候使用factory
關(guān)鍵字,定義工廠構(gòu)造函數(shù),從緩存中返回一個(gè)實(shí)例,或返回一個(gè)子類型的實(shí)例。
工廠構(gòu)造函數(shù)不能訪問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);// 命名構(gòu)造函數(shù)
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');// 相同對象直接訪問緩存
}
方法
實(shí)例方法
實(shí)例方法可以訪問實(shí)例變量和this
。
Getters and setters
get()
和set()
方法是Dart 語言提供的專門用來讀取和寫入對象的屬性的方法。每一個(gè)類的實(shí)例變量都有一個(gè)隱式的getter
和可能的setter
(如果字段為final
或const
,只有getter
)。
像++
之類的操作符,無論是否明確定義了getter,都按預(yù)期的方式工作。為了避免意想不到的副作用,操作符調(diào)用getter一次,將其值保存在臨時(shí)變量中。
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);
}
抽象方法
抽象方法,只有函數(shù)聲明,沒有函數(shù)體,以分號;
結(jié)尾,作為接口在其他類中實(shí)現(xiàn)。調(diào)用抽象方法會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。如果在不是抽象類中定義抽象方法會(huì)引發(fā)warning
,但不會(huì)阻止實(shí)例化。
abstract class Doer {
...
void doSomething(); // 定義抽象方法
}
class EffectiveDoer extends Doer {
void doSomething() {
// ...Provide an implementation, so the method is not abstract here...
}
}
可重寫的運(yùn)算符
下面是可重寫的運(yùn)算符,在運(yùn)算符前面使用operator
關(guān)鍵字。
如果重寫==
,需要重寫Object
的hashCode
getter。
< | + | { | [] |
---|---|---|---|
> | / | ^ | []= |
<= | ~/ | & | ~ |
>= | * | << | == |
– | % | >> |
抽象類
使用abstract
關(guān)鍵字定義一個(gè)抽象類,抽象類不能實(shí)例化。抽象類通常用來定義接口。
假如需要將抽象類實(shí)例化,需要定義一個(gè)factory constructor
。
抽象類通常會(huì)包含一些抽象的方法。
abstract class AbstractContainer { // 抽象類
// ....
void updateChildren(); // 抽象方法
}
隱式接口
每一個(gè)類都隱式的定義一個(gè)接口,這個(gè)接口包含了這個(gè)類的所有實(shí)例成員和它實(shí)現(xiàn)的所有接口。
一個(gè)類可以實(shí)現(xiàn)一個(gè)或多個(gè)(用,
隔開)接口,通過implements
關(guān)鍵字。
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
來創(chuàng)造子類,使用super
來指向父類。
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ...
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ...
}
子類可以重載實(shí)例方法,getter和setter。使用@override
注解重載方法。
class A {
@override
void noSuchMethod(Invocation mirror) {
print('You tried to use a non-existent member:' +
'${mirror.memberName}');
}
}
如果使用noSuchMethod()
來實(shí)現(xiàn)每一個(gè)可能的getter,setter和一個(gè)或多個(gè)類型的方法,可以使用@proxy
注解來避免警告:
@proxy
class A {
void noSuchMethod(Invocation mirror) {
// ...
}
}
如果知道編譯時(shí)類型,可以不使用@proxy
,只需要使類實(shí)現(xiàn)這些類型。
class A implements SomeClass, SomeOtherClass {
void noSuchMethod(Invocation mirror) {
// ...
}
}
枚舉類型
枚舉類型是一種特殊的類,通常用來表示一組固定數(shù)字的常量值。
使用enum
關(guān)鍵字聲明枚舉類型。
enum Color {
red,
green,
blue
}
每個(gè)枚舉類型都有一個(gè)index
的getter,返回以0開始的位置索引,每次加1。
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
使用values
關(guān)鍵字獲取枚舉的所有值,返回一個(gè)列表。
print(Color.values); // [Color.red, Color.green, Color.blue]
在switch語句中使用枚舉,必須在case語句中判斷所有的枚舉,否則會(huì)獲得警告。
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: //必須完成所有判斷,否則會(huì)引發(fā)warning
print(aColor); // 'Color.blue'
}
枚舉類型有以下限制:
- 不能繼承,mixin,或?qū)崿F(xiàn)一個(gè)枚舉。
- 不能顯式的實(shí)例化一個(gè)枚舉。
minxins
mixins是一種在多個(gè)類層次結(jié)構(gòu)中,重用一個(gè)類的代碼的方法。使用with
關(guān)鍵字后跟多個(gè)mixin名。
class Musician extends Performer with Musical {
// ...
}
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
class變量和方法
使用static
關(guān)鍵字來實(shí)現(xiàn)類范圍內(nèi)變量和方法。
- 靜態(tài)變量
靜態(tài)變量在使用時(shí)初始化。
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');
}
- 靜態(tài)方法
靜態(tài)方法不能被實(shí)例調(diào)用,不能訪問this
,只能被類名調(diào)用。
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
}
對于通用或廣泛使用的工具和功能,應(yīng)該使用頂級函數(shù),而不是靜態(tài)方法。
可以使用靜態(tài)方法作為編譯時(shí)常量。例如,可以給一個(gè)常量構(gòu)造函數(shù),傳遞一個(gè)靜態(tài)方法作為參數(shù)。
泛型(Generics)
使用<...>
的方式來定義泛型。
為什么使用泛型
- 雖然Dart 語言中類型是可選的,但是明確的指明使用的是泛型,會(huì)讓代碼更好理解。
var names = new List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
// ...
names.add(42); // Fails in checked mode (succeeds in production mode).
- 使用泛型減少代碼重復(fù)。
代碼片段一:
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
類型參數(shù)化。
- 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'
};
在構(gòu)造函數(shù)中參數(shù)化類型
var names = new List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
var nameSet = new Set<String>.from(names);
var views = new Map<int, View>();
泛型集合及它們所包含的類型
dart的泛型類型是具體的,在運(yùn)行時(shí)包含它們的類型信息。
var names = new List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
然而,is
表達(dá)式檢查的是集合的類型,而不是里面的對象。在生產(chǎn)模式下,一個(gè)List<Stirng>
可能有很多不是string
的條目在里面。解決辦法是檢查每一項(xiàng),或者包裹在一個(gè)異常處理程序中。
限制參數(shù)化類型
當(dāng)實(shí)現(xiàn)一個(gè)泛型時(shí),如果需要限制它參數(shù)的類型,可以使用extends
關(guān)鍵字。
// 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
指令可以方便的創(chuàng)建一個(gè)模塊或分享代碼。一個(gè)Dart 庫不僅能夠提供相應(yīng)的API,還可以包含一些以_
開頭的私有變量僅在庫內(nèi)部可見。
每一個(gè)Dart 應(yīng)用都是一個(gè)庫,即使它沒有使用library
指令。庫可以方便是使用各種類型的包。
使用庫(Libraries and visibility)
使用import
指定怎樣的命名空間,從一個(gè)庫引用另一個(gè)庫。
import
唯一要求的參數(shù)是指定庫的URI。
- dart內(nèi)置庫,URI組合
dart:
- 其他庫,使用文件路徑或
package:
組合。package:
組合式通過包管理工具提供的。
import 'dart:html';
import 'package:mylib/mylib.dart';
指定一個(gè)庫的前綴
如果導(dǎo)入的庫擁有相互沖突的名字,使用as
為其中一個(gè)或幾個(gè)指定不一樣的前綴。
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.
導(dǎo)入庫的一部分
如果只需要使用庫的一部分內(nèi)容,使用show
或hide
有選擇的導(dǎo)入。
// 僅導(dǎo)入foo.
import 'package:lib1/lib1.dart' show foo;
// 除了foo都導(dǎo)入
import 'package:lib2/lib2.dart' hide foo;
延遲加載庫
延遲加載,也叫懶加載,允許應(yīng)用程序按需加載庫。使用延遲加載的場景:
- 減少程序初始化啟動(dòng)時(shí)間。
- 執(zhí)行A/B測試——嘗試替換一個(gè)算法的實(shí)現(xiàn)。
- 加載很少用的功能,比如可選的屏幕和對話框。
要延遲加載一個(gè)庫,首先必須使用deferred as
導(dǎo)入它。
import 'package:deferred/hello.dart' deferred as hello;
當(dāng)需要使用庫的時(shí)候,使用庫名調(diào)用loadLibrary()
。
greet() async {
// 使用await關(guān)鍵字暫停執(zhí)行,直到庫加載
await hello.loadLibrary();
hello.printGreeting();
}
可以在代碼中多次調(diào)用loadLibrary()
方法。但是實(shí)際上它只會(huì)被執(zhí)行一次。
使用延遲加載的注意事項(xiàng):
- 延遲加載的內(nèi)容只有在加載后才存在。
- Dart 隱式的將
deferred as
改為了deferred as namespace
。loadLibrary()
返回值是Future
。
異步支持(Asynchrony support)
使用async
函數(shù)和await
表達(dá)式實(shí)現(xiàn)異步操作。
當(dāng)需要使用一個(gè)從Future
返回的值時(shí),有兩個(gè)選擇:
- 使用
async
和await
。 - 使用
Future API
。
當(dāng)需要從一個(gè)Stream
獲取值時(shí),有兩個(gè)選擇:
- 使用
async
和異步的循環(huán)(await for
)。 - 使用
Stream API
。
代碼使用了async
和await
就是異步的,雖然看起來像同步代碼。
await lookUpVersion()
要使用await
,代碼必須在函數(shù)后面標(biāo)記為async
:
checkVersion() async {
var version = await lookUpVersion();
if (version == expectedVersion) {
// Do something.
} else {
// Do something else.
}
}
可以使用try,catch和finally
配合await
處理錯(cuò)誤。
try {
server = await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 4044);
} catch (e) {
// 無法綁定到端口時(shí)......
}
聲明異步函數(shù)
異步函數(shù)是一個(gè)被async
標(biāo)記的函數(shù)。
雖然異步的函數(shù)中可能執(zhí)行耗時(shí)的操作,但是函數(shù)本身在調(diào)用后將會(huì)立即返回,即使函數(shù)體一條語句也沒執(zhí)行。
checkVersion() async {
// ...
}
lookUpVersion() async => /* ... */;
給函數(shù)添加async
關(guān)鍵字將使函數(shù)返回一個(gè)Future
類型。
// 修改前是同步的
String lookUpVersionSync() => '1.0.0';
// 修改后 是異步的 函數(shù)體不需要使用Future API
// dart會(huì)在必要的時(shí)候創(chuàng)建Future對象
Future<String> lookUpVersion() async => '1.0.0';
在 Future 中使用 await 表達(dá)式
await expression
可以在異步函數(shù)中多次使用await
var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
在await
表達(dá)式中,表達(dá)式通常是一個(gè)Future
。如果表達(dá)式不是Future
類型,它將自動(dòng)被包裝為Future
類型。await expression
的返回值是一個(gè)對象。await表達(dá)式使執(zhí)行暫停,直到對象可用。
如果await
不工作,確保await
處于async
函數(shù)中。即使是在main函數(shù)中,也要標(biāo)記為async
。
main() async {
checkVersion();
// 這里使用了 await
print('In main: version is ${await lookUpVersion()}');
}
在Stream中使用異步循環(huán)
// expression的值必須是Stram類型
await for (variable declaration in expression) {
// Executes each time the stream emits a value.
}
異步循環(huán)的執(zhí)行流程如下:
- 等待 stream 發(fā)出數(shù)據(jù)。
- 執(zhí)行循環(huán)體,并將變量的值設(shè)置為發(fā)出的數(shù)據(jù)。
- 重復(fù)1.,2.直到stream 對象被關(guān)閉。
結(jié)束監(jiān)聽stram 可以使用break
或returen
語句,跳出for
循環(huán),取消訂閱stream。
如果異步循環(huán)不工作,確保是在一個(gè)async
函數(shù)中,如果使用異步循環(huán)在main()
函數(shù)中,也要確保main()
函數(shù)被標(biāo)記為async
。
main() async {
...
await for (var request in requestServer) {
handleRequest(request);
}
...
}
可調(diào)用類(Callable classes)
Dart 語言中為了能夠讓類像函數(shù)一樣能夠被調(diào)用,可以實(shí)現(xiàn)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 代碼都運(yùn)行在獨(dú)立的隔離空間中,每個(gè)隔離空間都有自己的內(nèi)存堆棧,確保每個(gè)隔離空間的狀態(tài)不會(huì)被其它的隔離空間訪問。
類型定義(Typedefs)
typedef
關(guān)鍵字,用來聲明一種類型,當(dāng)一個(gè)函數(shù)類型分配給一個(gè)變量時(shí),保留類型信息。
// 聲明一種 Compare類型
typedef int Compare(int a, int b);
int sort(int a, int b) => a - b;
main() {
assert(sort is Compare); // True!
}
當(dāng)前 typedef 僅用于函數(shù)類型。
元數(shù)據(jù)(Metadata)
元數(shù)據(jù)是以@
開始的修飾符。在@
后面接著編譯時(shí)的常量或調(diào)用一個(gè)常量構(gòu)造函數(shù)。
所有dart代碼中可用的三個(gè)注解:
- @deprecated 被棄用的
- @override 重載
- @proxy 代理
定義自己的元數(shù)據(jù)
通過library
來定義一個(gè)庫,在庫中定義一個(gè)相同名字的class
,然后在類中定義const
構(gòu)造方法。
// 定義
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');
}
元數(shù)據(jù)可以修飾library
(庫), class
(類), typedef
(類型定義), type parameter
(類型參數(shù)), constructor
(構(gòu)造函數(shù)), factory
(工廠函數(shù)), function
(函數(shù)), field
(作用域), parameter
(參數(shù)), 或者 variable declaration
(變量聲明)。
可以使用reflection
反射,在運(yùn)行時(shí)檢查元數(shù)據(jù)。
注釋(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) {
// ...
}
}