Dart 的單行註釋也是 //
,註釋內容最好是個完整的句子,一般以大寫字母開頭,以句點或其餘標點符號結束:html
// Not if there is nothing before it.
if (_chunks.isEmpty) return false;
複製代碼
/* ... */
只適合用來注掉某一段暫時不用的代碼,不適合當成註釋使用。
///
是 Dart 推薦使用的文檔註釋語法,dartdoc 會根據文檔註釋生成 HTML 文檔,使用方括號能夠連接到一個類、方法、字段等位置:git
/// Feeds your llama [Food].
///
/// The typical llama eats one bale of hay per week.
void feed(Food food) {
// ...
}
複製代碼
因爲歷史緣由, Dart 也支持 JavaDoc 樣式的文檔註釋 /** ... */
,但這種類型的文檔註釋會產生兩個無實際內容的行,對於使用 *
標誌列表項等場景並不方便,因此推薦使用 ///
文檔註釋。github
能夠賦值給變量的都是對象,包括數字、函數和 null
對象,對象都是類的實例,都繼承自 Object
。
變量存的是引用。
雖然 Dart 是強類型語言,可是類型聲明是可選的,由於 Dart 能夠推斷出變量到底是什麼類型的。
可使用 var
關鍵字聲明變量而無需指定具體類型,如 var name = 'Bob';
,var number = 42;
。
有些操做須要處理任何可能的對象,如 log()
方法須要調用給定對象的 toString()
。而在 Dart 中有兩種類型: Object
和 dynamic
,Object
適合表示能夠是任何對象的場景,而 dynamic
適合表示更復雜的類型,如意味着 Dart 的類型系統已經不足以表示的一系列容許的類型,或者值來自 interop 或 其餘超過靜態類型系統範圍的類型,或者你想明確地聲明運行時動態處理的變量:express
void log(Object object) {
print(object.toString());
}
/// Returns a Boolean representation for [arg], which must
/// be a String or bool.
bool convertToBool(dynamic arg) {
if (arg is bool) return arg;
if (arg is String) return arg == 'true';
throw new ArgumentError('Cannot convert $arg to a bool.');
}
複製代碼
若是變量永遠不會改變,可使用 final
或 const
去聲明,final 變量只能賦值一次,const 變量是編譯時常量(也是隱式 final
的),final 的頂級類變量是在其第一次被使用時初始化的。實例變量能夠是 final
的,但不能是 const
的。
const
不單能聲明常量,還能夠建立常量值,或者聲明能夠建立常量值的構造器。編程
num
類型有兩個子類型 int
和 double
,int
型位數不超過64位,在 Dart VM 上是 -263 到 263 - 1,double
是64位浮點數,遵循 IEEE 754 標準。
String
類型是 UTF-16 編碼,可使用單引號也可使用雙引號,所以能夠靈活的避免字符串限定符。能夠利用 ${expression}
在字符串中使用表達式的值,若是表達式是個標識符,那麼 {}
能夠省略。字符串拼接可使用緊鄰的字符串或者 +
號:api
var s1 = 'String '
'concatenation'
" works even over line breaks.";
var s2 = 'The + operator ' + 'works, as well.';
複製代碼
多行的字符串可使用三個單引號或三個雙引號包裹。 可使用前綴 r
建立原始字符串:緩存
// 在原始字符串中,即便是 \n 也再也不是特殊字符了
var s = r"In a raw string, even \n isn't special.";
複製代碼
Dart 的集合 API 包含 lists, sets, 和 maps。List
可使用傳統構造器建立,也可使用 []
語法建立,Map
可使用傳統構造器建立,也可使用 {}
語法建立:bash
// Use a List constructor.
var vegetables = new List();
// Or simply use a list literal.
var fruits = ['apples', 'oranges'];
// Maps can be built from a constructor.
var searchTerms = new Map();
// Maps often use strings as keys.
var hawaiianBeaches = {
'Oahu': ['Waikiki', 'Kailua', 'Waimanalo'],
'Big Island': ['Wailea Bay', 'Pololu Beach'],
'Kauai': ['Hanalei', 'Poipu']
};
複製代碼
List
和 Set
都實現了 Iterable
,能夠經過迭代器去遍歷,而 Map
能夠經過它的keys
和 values
屬性獲取迭代器。markdown
Dart 是真正的面嚮對象語言,因此即便是函數也是對象,類型是 Function
。函數做爲一類對象(first-class objects),能夠做爲參數傳遞給其餘函數。
若是函數體只包含一個表達式語句,可使用 =>
簡寫,如:閉包
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
複製代碼
能夠簡寫成:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
複製代碼
這種箭頭一般稱做胖箭頭(fat arrow)。
區分 expression 和 statement 有時仍是挺重要的,不包含 if
等控制流語句的程序元素均可以稱之爲表達式,包括操做符表達式,return
子句,throw
子句,print
等函數的調用。
可選參數有兩種,命名可選參數和有序可選參數,可選參數必須放在全部必須參數的後面。命名可選參數的列表使用 {param1, param2, …}
聲明,使用 paramName: value
調用。有序可選參數的列表使用 [param1, param2, …]
聲明:
void enableFlags(String styleName, {bool bold, bool hidden}) {
}
...
enableFlags("NORMAL", hidden: false);
複製代碼
String say(String from, String msg, [String device]) {
}
...
say('Bob', 'Howdy');
say('Bob', 'Howdy', 'smoke signal');
}
複製代碼
可選參數能夠經過 =
指定默認參數值,這個默認參數值必須是編譯時常量,若是不指定默認參數值,那麼默認參數值爲 null
,因此不要顯示地設置默認值爲 null
:
void enableFlags({bool bold = false, bool hidden = false}) {
}
...
enableFlags(bold: true);
複製代碼
每一個 app 都必須有一個頂層 main()
函數做爲 app 的入口, main()
函數的參數列表是可選的 List<String>
,返回值是 void
。
沒有名字的函數有時會稱爲 匿名函數(anonymous function)、lambda 表達式(lambda) 或 閉包(closure),匿名函數的聲明與普通函數相似:
([[Type] param1[, …]]) {
codeBlock;
};
複製代碼
如:
var functionList = [];
functionList.add((int element) {
print("element:" + element.toString());
});
複製代碼
Dart 是一個 lexically scoped 語言,即 詞法做用域/靜態做用域 語言。也就是說,變量的做用域是根據它所處代碼的佈局位置靜態決定的,你能夠根據大括號去判斷變量的做用域。
閉包(closure)就是一個函數對象能夠訪問它詞法做用域內的變量,即便這個函數在原始範圍外被使用。例如,下面的例子,makeAdder()
會捕獲 addBy
. 不管他返回的函數在哪,他都會記住 addBy
:
/// 返回一個匿名函數,匿名函數的返回值是
/// 匿名函數的參數加上 [addBy].
Function makeAdder(num addBy) {
return (num i) => addBy + i;
}
void main() {
// 建立一個返回值是參數值加2的函數
var add2 = makeAdder(2);
// 建立一個返回值是參數值加4的函數
var add4 = makeAdder(4);
assert(add2(3) == 5);
assert(add4(3) == 7);
}
複製代碼
全部函數都有返回值,若是不指定返回值,默認會隱式添加 return null;
到函數體中。
5 / 2 的值是浮點數 2.5,而5 ~/ 2 的值纔是整型的 2。
判斷對象類型用 is
或 is!
操做符,使用 as
操做符進行類型轉換:
if (emp is Person) {
emp.firstName = 'Bob';
}
// 能夠簡寫成下面的語句,可是若是emp 不是 Person 類型的
// 那麼下面的語句會拋出異常
(emp as Person).firstName = 'Bob';
複製代碼
使用 ??=
操做符能夠給一個值爲 null
的變量賦值。使用 ?.
能夠避免左邊表達式的值爲空致使的異常,如 foo?.bar
在 foo
不爲空時返回 bar
屬性,在 foo
爲空時返回 null
。
級聯符號 ..
可讓你連續操做相同的對象,不單能夠連續地調用函數,還能夠連續地訪問方法,這樣作能夠避免建立臨時變量,從而寫出更流暢的代碼,流式編程更符合現代編程習慣和編程風格:
final addressBook = (new AddressBookBuilder()
..name = 'jenny'
..email = 'jenny@example.com'
..phone = (new PhoneNumberBuilder()
..number = '415-555-0100'
..label = 'home')
.build())
.build();
複製代碼
嚴格意義上說,級聯符號不是操做符,而是 Dart 語法的一部分。
若是循環(迭代)的對象是 Iterable
對象,能夠利用它的 forEach()
方法對集合中的元素進行操做,forEach()
方法的參數是一個函數,它會按序應用集合中的每一個元素給這個函數:
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
list.forEach(printElement);
複製代碼
像 List
和 Set
這樣的 Iterable 類還支持 for-in
循環:
var collection = [0, 1, 2];
for (var x in collection) {
print(x); // 0 1 2
}
複製代碼
拋出的異常能夠是任意對象,但最好是 Error
或 Exception
對象:
throw new FormatException('Expected at least 1 section');
...
throw 'Out of llamas!';
複製代碼
捕獲異常可使用 on
或 catch
或者同時使用:
try {
breedMoreLlamas();
} on OutOfLlamasException {
// 捕獲 OutOfLlamasException 類型的異常,不須要使用異常對象
buyMoreLlamas();
} on Exception catch (e) {
// 捕獲 Exception 異常,打印異常對象
print('Unknown exception: $e');
} catch (e) {
// 捕獲並處理任何類型的 thrown 對象
print('Something really unknown: $e');
}
複製代碼
catch()
能夠有兩個參數,第一個是拋出的異常對象,第二個是堆棧跟蹤對象。若是捕獲異常並處理後還容許該異常繼續傳播,可使用 rethrow
關鍵字:
final foo = '';
void misbehave() {
try {
foo = "You can't change a final variable's value.";
} 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}.');
}
}
複製代碼
finally
子句會在 catch
子句執行完執行。若是沒有 catch
子句,那麼會在finally
子句運行完纔開始傳播異常。
Dart 的繼承是基於 mixin 的繼承,也就是說,雖然每一個類(除了 Object
類)有且只有一個超類,但類體仍然能夠在多個類層級上重用。
構造器的名字能夠是 ClassName
(無名構造器),也能夠是 ClassName.identifier
(命名構造器),實例化時的 new
關鍵字能夠省略。
全部的實例變量都會自動生成隱式的 getter 方法,非 final 的實例變量也會自動生成隱式的 setter 方法,使用 get
和 set
關鍵詞能夠建立額外的屬性和其 getter/setter 方法的實現。
Dart 有個語法糖能夠方便地將構造器參數賦值給實例變量(在構造器體執行以前):
class Point {
num x, y;
Point(this.x, this.y);
}
複製代碼
若是不聲明構造器,會隱式包含一個無名無參構造器,並調用父類的無名無參構造器。
子類不能繼承父類的構造器,因此,若是若是子類想利用父類的命名構造器建立實例,就必須實現父類的命名構造器。若是父類沒有無名無參構造器,那麼你就必須手動調用父類的一個構造器:
class Person {
String firstName;
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
}
複製代碼
初始化的順序爲: 初始化列表 → 父類無參構造器 → 主類無參構造器
能夠在構造器函數體執行前初始化實例變量,初始化列表使用逗號隔開,初始化列表特別適合初始化 final 字段:
class Point {
final num x;
final num y;
final num distanceFromOrigin;
Point(x, y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}
複製代碼
重定向到同類其餘構造器:
class Point {
num x, y;
// The main constructor for this class.
Point(this.x, this.y);
// Delegates to the main constructor.
Point.alongXAxis(num x) : this(x, 0);
}
複製代碼
若是類的對象永遠不會改變,就可讓這個對象成爲編譯時常量,只須要定義 const
構造器並確保全部的實例變量是 final
的:
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
複製代碼
可使用 factory
關鍵詞聲明工廠構造器,工廠構造器不必定要建立一個新的實例,能夠直接返回緩存的實例,也能夠返回子類型的實例,如:
class Logger {
final String name;
bool mute = false;
// _cache is library-private, thanks to
// the _ in front of its name.
static final Map<String, Logger> _cache =
<String, Logger>{};
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = new Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
複製代碼
可使用 abstract
修飾符聲明一個不能直接實例化的抽象類,抽象類能夠包含抽象方法,抽象方法沒有方法體,以分號結尾。
每一個類都隱式地定義了一個接口,該接口包含該類的全部實例成員及其實現的全部接口。能夠經過 implements
子句實現一或多個接口,以逗號隔開。
使用 extends
建立子類,使用 super
引用父類,使用 @override
註解有意地重載成員。
在面向對象的語言中,多繼承是個很是糟糕的設計,由於多繼承會產生一系列問題,如:
多繼承會使類之間的關係更加複雜。若是一個類有多個超類,超類又可能有多個超類,那麼類之間的關係將會變得尤其複雜,你很難理清一個類的超類到底有哪些,繼承關係又是怎樣的。
多繼承可能會出現菱形繼承的狀況,這種狀況下會出現二義性,雖然 C++ 使用虛繼承的方式能夠暫時避免這種二義性,但會使程序可讀性和可調試性變差。
因此現代的面嚮對象語言都不會容許多繼承這種狀況存在,其實繼承的目的無非是爲了重用並拓展類的功能,而經過繼承來重用類體並非一個好的方式,首先繼承會「白箱複用」,會將超類細節暴露給子類,無論子類願不肯意。若是超類更改,其全部子類也不得不改變。並且繼承在運行時的行爲不易改變。
因此儘量少用繼承,多用組合。繼承是 is-a 的關係,而組合是 has-a 的關係。
Mixin 的概念源於 LISP 社區,在那裏是指被設計用來與各個超類一塊兒工做的類。對 Mixin 介紹比較詳細的是 Mixins in Strongtalk 這篇論文。
一個類會隱式地定義一個 mixin, mixin 被類體定義,與類和它的超類構成函數關係,類實際上就是 mixin 的應用,是將其隱式定義的 mixin 應用於其超類的結果。Mixin應用 與 函數應用 相似,從數學上來講,一個 mixin 能夠當作是超類到子類的函數,也就是說,將 做用於超類 ,結果是新的 子類,在研究資料裏一般寫做 。所以對於 mixin 的組合來講: 。 一個類隱式定義的 mixin 一般只會在給定超類定義處應用一次,因此爲了讓 mixin 可以應用不一樣的超類,咱們須要獨立於任何特定超類來聲明 mixin,或者將類隱式定義的 mixin 剝離出來並在原始聲明以外重用它。
Mixin 能夠經過普通類定義的方式隱式定義,但要想提取 mixin 就必需要保證類不能聲明構造器,這也避免了因爲在繼承鏈上傳遞構造器參數引發的各類併發症。
abstract class Collection<E> {
Collection<E> newInstance();
Collection<E> map((f) {
var result = newInstance();
forEach((E e) { result.add(f(e)); });
return result;
});
void forEach(void f(E element)) {
// ...
}
void add(E element) {
// ...
}
}
abstract class DOMElementList<E> = DOMList with Collection<E>; abstract class DOMElementSet<E> = DOMSet with Collection<E>; 複製代碼
在這裏,Collection
就是一個隱式聲明瞭 mixin 的普通類,它沒有構造器。而 DOMElementList
和 DOMElementSet
類就是 mixin 的應用,它們被特殊的類聲明格式定義,也就是經過 with
子句聲明 mixin 到 父類的應用。這兩個類也是抽象的,由於他們沒有實現 Collection
類的抽象方法 newInstance()
。
也就是說,DOMElementList
就是 Collection mixin ▷ DOMList,而 DOMElementSet
就是 Collection mixin ▷ DOMSet。
這樣的好處是,Collection
類的代碼能夠在多個類層次上共享。咱們這裏列舉了兩個類層次,一個是以 DOMList
爲根的,一個是以 DOMSet
爲根的。咱們不須要複製粘貼Collection
類的代碼,而 Collection
的任何更改都會傳播到這兩個類層次中,這樣也極大地解放了代碼的維護。
上面的例子只是簡單說明了一種形式的 mixin 應用,應用的結果是一個有名字的類。而另外一種形式,with
子句後跟上以逗號分隔的標識符列表,全部的標識符必須表示一個類,這種形式下,多個 mixin 會被組合並應用到 extends
子句的超類中,從而產生一個匿名超類,即:
class DOMElementList<E> extends DOMList with Collection<E> {
DOMElementList<E> newInstance() => new DOMElementList<E>();
}
class DOMElementSet<E> extends DOMSet with Collection<E> {
DOMElementSet<E> newInstance() => new DOMElementSet<E>();
}
複製代碼
這裏的 DOMElementList
再也不是 Collection mixin ▷ DOMList 的應用,而是一個新類,它的超類是這個應用。
若是 DOMList
有非平凡構造器,那麼構造將會是這樣:
class DOMElementList<E> extends DOMList with Collection<E> {
DOMElementList<E> newInstance() => new DOMElementList<E>(0);
DOMElementList(size): super(size);
}
複製代碼
每一個 mixin 都有屬於本身的被獨立調用的構造器,超類也是如此。因爲 mixin 的構造器不能被聲明,對它的調用能夠用 ;
語法省略,在底層實現時,這個調用會被放在初始化列表的開始處。
爲了讓多個 mixin 應用到一個類是而又不引入多箇中間聲明,可使用這種方便的語法糖:
class Person {
String name;
Person(this.name);
}
class Maestro extends Person with Musical, Aggressive, Demented {
Maestro(name):super(name);
}
複製代碼
在這裏,Maestro
的超類就是這樣的 mixin 應用:
Demented mixin ▷ Aggressive mixin ▷ Musical mixin ▷ Person
mixin 與普通繼承同樣,隱私性取決於其聲明的位置。mixin 的應用的靜態元素一樣不能被繼承使用,Dart 中靜態元素是不能被繼承的。
那 mixin 應用後的實例類型究竟是什麼類型呢?毫無疑問,它必須是命名 mixin 類的子類型,也必須是原來類的子類型。原來的類有它本身的超類,爲了確保指定的 mixin 應用 兼容原來的類,Dart 會給 with
子句的類額外的要求。
若是類 A 定義時的 with
子句是 mixin M,而 M 又是繼承的 類 K,那麼類 A 就必須支持類 K 的接口。
class S {
twice(int x) => 2 * x;
}
abstract class I {
twice(x);
}
abstract class J {
thrice(x);
}
class K extends S implements I, J {
int thrice(x) => 3* x;
}
class B {
twice(x) => x + x;
}
class A = B with K; 複製代碼
A 必須支持 K 的超類 S 的隱式接口,以確保 A 確實是 M 的子類型。K 必須實現 twice()
以知足 I 的須要,同時必須實現 thrice()
以知足 J 的須要,顯然 K 已經知足了這些須要。
如今咱們聲明瞭 A,咱們從 K 的 mixin 中 拿到了 thrice()
的實現,但這個 mixin 沒有提供 twice()
的實現,幸運的是,B 有這個實現,因此總的來講,A 確實知足了 I,J 和 S 的須要。
相反,對於這樣的類 D:
class D {
double(x) => x+x;
}
class E = D with K; 複製代碼
會收到一個警告,由於 E 沒有 twice()
方法,所以不知足 I 和 S,也就不能使用預期的 K 了。
若是一個類有泛型參數,那麼它的 mixin 也必須有相同類型的參數。
在 1.13 版本以後,Mixin 能夠繼承普通類而不要求必定是 Object,Mixin 可使用 super.method()
。
能夠經過 operator
關鍵字重載包括 +
,>
,==
在內的操做符,但要注意,重載 ==
操做符的同時也要重載 hashCode
的 getter 方法:
class Person {
final String firstName, lastName;
Person(this.firstName, this.lastName);
// Override hashCode using strategy from Effective Java,
// Chapter 11.
@override
int get hashCode {
int result = 17;
result = 37 * result + firstName.hashCode;
result = 37 * result + lastName.hashCode;
return result;
}
// You should generally implement operator == if you
// override hashCode.
@override
bool operator ==(dynamic other) {
if (other is! Person) return false;
Person person = other;
return (person.firstName == firstName &&
person.lastName == lastName);
}
}
複製代碼
使用 enum
關鍵詞定義枚舉類型,枚舉類型的值有一個 index
getter,枚舉值的列表能夠經過它的 values
常量獲取,枚舉支持 switch
語句,你不能繼承,mix in 或者實現枚舉類,不能顯式實例化枚舉:
enum Color { red, green, blue }
assert(Color.red.index == 0);
List<Color> colors = Color.values;
複製代碼
每一個 Dart app 都是一個 library,即便它沒使用 library
指令,library 不只提供 API,仍是個隱私單元,如下劃線(_)開頭的標識符只在當前 library 內可見。
使用 import
指令能夠指定在另外一個 library 範圍內如何使用另外一個 library 的命名空間。對於內置的 library,URI 是特定的 dart:
scheme,而其餘的 URI 能夠是文件系統路徑或者 package:
scheme:
import 'dart:html';
import 'package:test/test.dart';
複製代碼
若是兩個 library 的標識符衝突,可使用前綴加以區分:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// Uses Element from lib1.
Element element1 = new Element();
// Uses Element from lib2.
lib2.Element element2 = new lib2.Element();
複製代碼
若是隻想引入 library 的部分功能,須要使用 show
或 hide
指定:
// Import only foo.
import 'package:lib1/lib1.dart' show foo;
// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
複製代碼
若是想要懶加載某個 library,先在 import 時經過 deferred as
指定 library 的標識符,而後在須要使用它的時候調用標識符的 loadLibrary()
:
import 'package:greetings/hello.dart' deferred as hello;
...
Future greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
複製代碼
此時 await
關鍵字會暫停程序執行,直到 library 加載成功,能夠屢次調用 loadLibrary()
,由於 library 只會加載一次。
Dart 爲異步提供了 Future
和 Stream
兩個類,Future 就像未來某個時刻提供結果的承諾,Stream 是獲取值序列(例如事件序列)的一種方式。
不少時候沒有必要直接使用 Future 或 Stream API,而是使用 async
和 await
關鍵字進行異步編程,這樣更容易理解,並且代碼看起來也更像同步代碼。
await
必須在異步函數中使用,用來等待異步函數返回的結果,如:
Future checkVersion() async {
var version = await lookUpVersion();
// Do something with version
}
複製代碼
雖然異步函數能夠執行耗時操做,但它不用等待耗時操做完成,這個異步函數會執行直到遇到第一個 await
表達式,而後返回 Future 對象,在 await
表達式執行完後再繼續執行。
能夠在一個異步函數裏 await
屢次,也能夠 try/catch await
的代碼。
在 await expression
語句中,expression
的值一般是個 Future,若是不是,那麼它會被自動包裝成 Future。await expression
會暫停其以後代碼的執行,直到返回的對象可用。
若是異步函數不須要返回有用值,能夠那麼返回類型應該是 Future<void>
。
能夠用 await for
異步等待 stream 中的全部結果,但不能是 UI 事件流這樣的 stream,由於這樣的 stream 是無限的。
Dart 內置了兩種生成器函數,同步的生成器函數返回 Iterable
對象,異步的生成器函數返回 Stream
對象,使用 sync*
/async*
指定同步異步,使用 yield
交付值,還能夠經過 yield*
遞歸生成:
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
Iterable<int> naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
}
}
複製代碼
自定義註解就像聲明普通類同樣:
library todo;
class Todo {
final String who;
final String what;
const Todo(this.who, this.what);
}
複製代碼
as
子句)要所有小寫並用下劃線分隔。HttpConnectionInfo
uiHandler
IOStream
HttpRequest
Id
DB
複製代碼
lib
目錄下的 library時,要使用相對路徑。${expression}
插值便可。.length
判斷集合是否爲空可能會很是慢,因此使用 .isEmpty
或 .isNotEmpty
判斷集合狀況是最好的選擇,更快也更直觀。var copy1 = iterable.toList();
var copy2 = new List.from(iterable);
複製代碼
第一種能夠保存原始對象的類型參數,而第二種的運行時類型是 dynamic
的,不過能夠經過 new List<int>.from(numbers)
這樣的方式指定。whereType()
:var objects = [1, "a", 2, "b", 3];
var ints = objects.whereType<int>();
複製代碼
cast()
,由於該方法會返回 lazy 集合而且每次操做都要檢查元素類型,若是集合元素和元素操做特別多那麼 lazy 驗證和打包的開銷會特別大,因此 stuff.toList().cast<int>()
徹底能夠用 new List<int>.from(stuff)
替代,stuff.map((n) => 1 / n).cast<double>()
徹底能夠用 stuff.map<double>((n) => 1 / n)
替代。=>
簡化簡單的代碼邏輯,如:num get x => center.x;
set x(num value) => center = new Point(value, center.y);
bool isReady(num time) => minTime == null || minTime <= time;
複製代碼