重要概念
- 可以放在變量中的都是對象,所有對象都是類的實例,包括數(shù)字,函數(shù),null都是對象, 所有對象都是繼承自 Object類
- Dart類型是強類型語言,但是Dart也支持類型推斷,如果要明確說明不需要任何類型琐脏,請使用特殊類型dynamic妒潭。
- Dart支持泛型,比如
List<int>
(包含int的數(shù)組),List<dynamic>
(包含任意類型對象的數(shù)組) - Dart支持top-level函數(shù)(比如main()),以及方法(靜態(tài)方法和實例方法). 也可以在函數(shù)中創(chuàng)建函數(shù)(內(nèi)嵌函數(shù)或本地函數(shù))
- 同樣的,Dart支持top-level變量,以及綁定到類或?qū)ο蟮淖兞?靜態(tài)變量和實例變量),實例變量有時候被稱為 字段或者屬性
- 不像Java,Dart沒有
public
,protected
,private
關(guān)鍵字.如果標識符以下劃線(_)開頭,則它就是私有變量 - 標識符可以以字母或下劃線(_)開頭
變量
var name = 'Bob';
變量存儲引用, name變量包含了一個String對象的引用,該對象的值是'Bob'.
name被推斷為String類型,但是可以通過指定來改變其類型,如果對象不局限于單一類型,可以指定為Object或者dynamic
dynamic name = 'Bob';
默認值
未初始化的變量的初始值為null
,即使數(shù)字類型的初始值也是null
,因為在Dart中, everything is object!
int lineCount;
assert(lineCount == null);
重點: 生產(chǎn)環(huán)境下,代碼會忽略 assert()調(diào)用, 開發(fā)環(huán)境中, 斷言會在條件為false時拋出異常
final 和 const
如果永遠不會修改變量,使用 final 或者 const。而不是使用var或者其他類型暂雹。
final 變量只會被設(shè)置一次,一個 const 變量是編譯時常量(const是隱式的final),final 修飾的top-level變量和類變量在第一次使用時初始化
Note: 實例變量可以是final,但不能是const鸭叙, final 實例變量必須在構(gòu)造函數(shù)體開始之前初始化(可以在變量聲明,構(gòu)造函數(shù)參數(shù)拣宏,或者在構(gòu)造函數(shù)的初始化列表)
final name = 'Bob'; // 沒有類型聲明
final String nickname = 'Bobby';
不能修改final變量的值
name = 'Alice'; // Error: a final variable can only be set once.
const 用于編譯時常量沈贝。 如果const在類中使用, 使用static const標識蚀浆。在聲明該變量的地方缀程,將值設(shè)置為編譯時常量搜吧,比如數(shù)字或者字符串市俊,
const bar = 1000000; // Unit of pressure (dynes/cm2)
const double atm = 1.01325 * bar; // Standard atmosphere
const 關(guān)鍵字并不只是用來聲明常量變量,也可以用來創(chuàng)建常量值滤奈,以及聲明創(chuàng)建常量值的構(gòu)造函數(shù)摆昧。任意變量都可以有一個常量值
var foo = const [];
final bar = const[];
const baz = []; // 等同于 const[]
可以從const聲明的初始化表達式中省略const,就像上面的 baz
蜒程。
可以改變 non-final,non-const變量的值绅你,即使該變量有過一個 const 值:
foo = [1,2,3];// 之前是 const[]
但是不能修改const變量的值:
baz = [42];// Error
同樣的伺帘,
var foo = const [];
foo這個數(shù)組的值也不能再改變,即foo不能添加/移除元素
foo.add(1);
error
Unsupported operation: Cannot add to an unmodifiable list
內(nèi)置類型
Dart語言特別支持以下類型:
- numbers
- strings
- booleans
- lists (也就是數(shù)組)
- sets
- maps
- runes (為了在字符串中表示Unicode字符)
- symbols
可以使用字面量來初始化這些類型忌锯。
Number
Dart數(shù)字有兩種類型
int
不超過64位的整數(shù)伪嫁,具體取決于平臺。在Dart VM上偶垮,值的范圍: 到 张咳。 編譯為JavaScript的Dart使用的是Javascript number,值的范圍是 到
double
64位(雙精度)浮點型數(shù)字似舵。由IEEE 754標準規(guī)定
int和double都是num的子類脚猾。 num類包含基礎(chǔ)運算符,比如+砚哗,-龙助,*,/蛛芥,以及abs(),ceil(),floor(),(在int類里有位運算符提鸟,比如<<)
在Dart2.1中,整型會在需要時自動轉(zhuǎn)為double類型
double z = 1;// 等同于 double z = 1.0;
下面是字符串轉(zhuǎn)為數(shù)字常空,反之亦然
// String -> int
var one = int.parse('1');
assert(one == 1);
// String -> double
var onePointOne = double.parse('1.1');
assert(onePointOne == 1.1);
// int -> String
String oneAsString = 1.toString();
assert(oneAsString == '1');
// double -> String
String piAsString = 3.14159.toStringAsFixed(2);
assert(piAsString == '3.14');
String
Dart字符串是一系列UTF-16代碼單元沽一。 可以使用單引號或雙引號來創(chuàng)建字符串:
var s1 = 'Single quotes work well for string literals.';
var s2 = "Double quotes work just as well.";
var s3 = 'It\'s easy to escape the string delimiter.';
var s4 = "It's even easier to use the other delimiter.";
可以使用${expression}將表達式的值放在字符串中,如果表達式是標識符漓糙,可以省略{}, 要獲得與對象對應(yīng)的字符串铣缠,可以調(diào)用對象的toString()
方法
可以使用相鄰的字符串或者 + 號來連接兩個字符串
// 相鄰的字符串
var s1 = 'String '
'concatenation'
" works even over line breaks.";
assert(s1 ==
'String concatenation works even over '
'line breaks.');
// +號
var s2 = 'The + operator ' + 'works, as well.';
assert(s2 == 'The + operator works, as well.');
創(chuàng)建多行字符串的方法:使用單引號/雙引號的三重引號
var s1 = '''
You can create
multi-line strings like this one.
''';
var s2 = """This is also a
multi-line string.""";
可以添加一個前綴 r來創(chuàng)建一個‘raw’字符串
var s = r'In a raw string, not even \n gets special treatment.';
bool
Dart中的布爾類行為bool,有兩個值:true,false;
Dart是類型安全的昆禽,也就意味著不會像OC那樣有非0即真的情況蝗蛙。條件表達式中必須明確傳遞一個布爾值。
// Check for an empty string.
var fullName = '';
assert(fullName.isEmpty);
// Check for zero.
var hitPoints = 0;
assert(hitPoints <= 0);
// Check for null.
var unicorn;
assert(unicorn == null);
// Check for NaN.
var iMeantToDoThis = 0 / 0;
assert(iMeantToDoThis.isNaN);
List
Dart中的數(shù)組醉鳖,有序集合捡硅。
var list = [1, 2, 3];
注意: Dart會類型推斷l(xiāng)ist的類型為 List<int>. 如果之后向其中添加其他非int的對象,編譯器會拋出錯誤
跟其他語言的數(shù)組一樣盗棵,List的下標索引從0開始壮韭,
var list = [1, 2, 3];
assert(list.length == 3);
assert(list[1] == 2);
list[1] = 1;
assert(list[1] == 1);
要創(chuàng)建編譯時常量的列表,請在列表前添加const:
var constantList = const [1,2,3];
// constantList[1] = 1; 會有錯誤纹因,因為列表是常量 不能再修改了
Dart2.3擴展運算符(...)和空值感知運算符(...?)喷屋,它提供了一種將多個元素插入到集合的簡潔方法。
比如瞭恰,可以使用擴展運算符將一個列表中的所有元素插入到另一個列表中
var list = [1,2,3];
var list2 = [0, ...list];
assert(list2.length == 4);
如果擴展運算符右邊的表達式有可能為null屯曹,可以使用空值感知運算符來避免異常。
var list;
var list2 = [0, ...?list];
assert(list2.length == 1);
Dart2.3也引入了collection if和collection for來創(chuàng)建集合。
下面是使用collection if的例子恶耽,列表包含三個/四個 item:
var nav = [
'Home',
'Furniture',
'Plants',
if (promoActive) 'Outlet'
];
使用collection for來操作列表item,然后將它們添加到另一個列表:
var listOfInts = [1, 2, 3];
var listOfStrings = [
'#0',
for (var i in listOfInts) '#$i'
];
assert(listOfStrings[1] == '#1');
Set
Dart中的無序集合是Set密任,
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
創(chuàng)建一個空的Set,請使用前面帶有類型參數(shù)的{},或者將{}賦給類行為Set的變量
var names = <String>{};
// Set<String> names = {}; // This works, too.
// var names = {}; // Creates a map, not a set.
Set還是Map? Map和Set的字面亮語法類似偷俭。由于Map首先出現(xiàn)浪讳,所以{}默認是Map類型。如果忘記了{}的類型注釋涌萤,Dart會創(chuàng)建一個Map<dynamic, dynamic>類型的對象驻债,也就是說{}默認會是Map類型
add()
,addAll()
方法來為Set添加元素:
var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);
使用.length
來獲取Set的元素個數(shù):
var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);
assert(elements.length == 5);
要創(chuàng)建一個編譯時常量的Set,在Set前添加const
final constantSet = const {
'fluorine',
'chlorine',
'bromine',
'iodine',
'astatine',
};
// constantSet.add('helium'); // error
類似于List形葬, Set也支持擴展運算符(...)和空值感知運算符(...?);
Map
類似于iOS中的字典合呐。key和value都可以是任意類型的對象,key只能出現(xiàn)一次笙以,value可以出現(xiàn)多次淌实。
var gifts = {
// Key: Value
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings'
};
var nobleGases = {
2: 'helium',
10: 'neon',
18: 'argon',
};
也可以使用Map的構(gòu)造函數(shù)來創(chuàng)建Map:
var gifts = Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
var nobleGases = Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';
向一個Map中添加鍵值對,類似于JS:
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds'; // Add a key-value pair
如果查詢一個不存在的key猖腕,會返回null:
var gifts = {'first': 'partridge'};
assert(gifts['fifth'] == null);
使用.length
來獲取Map元素的個數(shù):
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds';
assert(gifts.length == 2);
創(chuàng)建一個編譯時常量的Map拆祈,在Map字面量前使用const
final constantMap = const {
2: 'helium',
10: 'neon',
18: 'argon',
};
// constantMap[2] = 'Helium'; //error
Dart2.3之后,Map也支持...
和...?
Runes
在Dart中倘感,Runes是字符串的UTF-32代碼點放坏。
Unicode為世界上所有書寫系統(tǒng)中的每個字母,數(shù)字和符號定義了唯一的數(shù)值老玛。由于Dart字符串是 UTF-16編碼的序列粉怕,因此在字符串中表示32位的Unicode值需要特殊的語法捅厂。
表達Unicode代碼點的常用方法是 \uXXXX
胳蛮,其中XXXX是4位十六進制值半哟。 例如,心臟字符(?)是 \u2665
镜廉。 要指定多于或少于4個十六進制數(shù)字弄诲,請將值放在大括號中。 例如娇唯,笑的表情符號(??)是\u{1f600}
齐遵。
String類有幾個屬性可用于提取rune信息。codeUnitAt和codeUnit屬性返回16位的code unit塔插。使用runes屬性獲取字符串的runes
以下示例說明了runes(符文)梗摇、16位代碼單元和32位代碼點之間的關(guān)系:
main() {
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));
}
打印信息:
flutter: ??
flutter: [55357, 56399]
flutter: [128079]
flutter: ? ?? ?? ?? ?? ??
Symbol
Symbol對象表示Dart程序中聲明的運算符或標識符∮拥恚可能永遠也用不到Symbol留美。。伸刃。
要獲取標識符的符號谎砾,可以使用symbol字面量,#
后跟標識符:
#radix
#bar
Function
Dart是真面向?qū)ο蟮恼Z言捧颅,所以即使是函數(shù)也是一個對象景图,類型為Function。這意味著函數(shù)可以分配給變量或者作為參數(shù)傳遞給其他函數(shù)碉哑。
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
或者
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
=> expr是{ return expr; }的縮寫挚币,
函數(shù)可以有兩種類型的參數(shù):必需和可選。必需參數(shù)放在首位扣典,后面跟著一些可選參數(shù)妆毕,
可選參數(shù)
可選參數(shù)可以是位置參數(shù),也可以是命名參數(shù)
可選命名參數(shù)
當調(diào)用一個函數(shù)的時候贮尖,可以使用paramName: value的形式指定命名參數(shù):
enableFlags(bold: true, hidden: false);
當定義一個函數(shù)時笛粘,使用{param1, param2, …}的形式來制定命名參數(shù):
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold, bool hidden}) {...}
Flutter實例創(chuàng)建表達式可能變得復(fù)雜,因此Widget構(gòu)造函數(shù)僅使用命名參數(shù)湿硝。 這使得實例創(chuàng)建表達式更易于閱讀薪前。
你可以在任何Dart代碼(不僅是Flutter)中使用@required
來注釋一個命名參數(shù),來表明該參數(shù)是必須的:
const Scrollbar({Key key, @required Widget child})
當構(gòu)建Scrollbar時关斜,如果child
參數(shù)缺失示括,就會報一個錯誤
可選位置參數(shù)
把一組函數(shù)參數(shù)包括在[]
,來標記這些參數(shù)是可選位置參數(shù):
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
調(diào)用該函數(shù)-不傳可選參數(shù)
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
調(diào)用該函數(shù)-傳遞可選參數(shù)
assert(say('Bob', 'Howdy', 'smoke signal') ==
'Bob says Howdy with a smoke signal');
參數(shù)默認值
可以使用=
來為命名參數(shù)或者位置參數(shù)設(shè)置默認值。默認值必須是編譯時常量痢畜,如果沒有提供默認值垛膝,那默認值就是null。
舉個例子:
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold = false, bool hidden = false}) {...}
// bold will be true; hidden will be false.
enableFlags(bold: true);
下面示范了如何為位置參數(shù)設(shè)置默認值:
String say(String from, String msg,
[String device = 'carrier pigeon', String mood]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
if (mood != null) {
result = '$result (in a $mood mood)';
}
return result;
}
assert(say('Bob', 'Howdy') ==
'Bob says Howdy with a carrier pigeon');
也可以傳一個List或者Map作為默認值:
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()函數(shù)
每一個app都有一個頂層的main()函數(shù)丁稀,作為應(yīng)用的入口點。該函數(shù)返回值為void扔罪,并接收一個可選的List<String>
參數(shù)桶雀。
void main() {
querySelector('#sample_text_id')
..text = 'Click me!'
..onClick.listen(reverseText);
}
// Run the app like this: dart args.dart 1 test
void main(List<String> arguments) {
print(arguments);
assert(arguments.length == 2);
assert(int.parse(arguments[0]) == 1);
assert(arguments[1] == 'test');
}
函數(shù)作為first-class對象
函數(shù)本身可以作為一個參數(shù)傳遞給另一個函數(shù),比如:
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
// Pass printElement as a parameter.
list.forEach(printElement);
也可以把函數(shù)賦值給一個變量:
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
匿名函數(shù)
大多數(shù)函數(shù)是有名字的全肮,比如main()
或者printElement ()
,我們也可以創(chuàng)建一個沒有名字的函數(shù)-匿名函數(shù),或者創(chuàng)建lambda以及閉包辜腺。你可以把匿名函數(shù)賦值給一個變量评疗,方便添加到一個集合中百匆,或者從集合中刪除加匈。
匿名函數(shù)看起來類似于命名函數(shù)-零個或多個參數(shù)纵东,
以下示例定義了一個匿名函數(shù)篮迎,有一個無類型參數(shù)item甜橱。
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
print('${list.indexOf(item)}: $item');
});
詞匯范圍
Dart是一種詞法范圍的語言,這意味著變量的范圍是靜態(tài)確定的(只需通過代碼的布局)镊掖。
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);
}
}
}
閉包
閉包是一個函數(shù)對象亩进,它可以訪問其詞法范圍中的變量,即使該函數(shù)在其原始范圍之外使用也是如此主籍。
函數(shù)可以關(guān)閉周圍范圍中定義的變量。 在以下示例中幸海,makeAdder()捕獲變量addBy物独。 無論返回的函數(shù)在哪里,它都會記住addBy瞻凤。
/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(num addBy) {
return (num i) => addBy + i;
}
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);
}
函數(shù)相等
這里有個例子來測試頂層函數(shù),靜態(tài)函數(shù)蛛壳,以及實例函數(shù)的相等性:
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);
}
返回值
所有的函數(shù)都有返回值,如果沒有指定返回值忧吟,則返回null溜族。
foo() {}
assert(foo() == null);
操作符
描述 | 操作符 |
---|---|
一元后綴 | expr++?? expr--?? ()?? []?? ? .??? ?. |
一元前綴 | -expr?? !expr?? ~expr?? ++expr?? --expr |
乘法 | * ?? /?? %?? ~/ |
加法 | +????? - |
位移 | <<???? >>???? >>> |
按位與 | & |
按位異或 | ^ |
按位或 | | |
關(guān)系和類型測試 | >= ??? >?? <=??? < ?? as ?? is?? is! |
相等 | == ????????? != |
邏輯與 | && |
邏輯或 | |
if null | ?? |
條件 | expr1 ? expr2 : expr3 |
級聯(lián) | .. |
賦值 | =??? *= ??? /=??? +=?? -=??? &=??? ^= 等 |
這里有幾個操作符的用法
a++
a + b
a = b
a == b
c ? a : b
a is T
在上述表格中厕倍,每個操作符都比其下一行的操作符有更高的優(yōu)先級讹弯。比如坏挠,乘法運算符%比相等運算符==有更高的優(yōu)先級(因此在==之前先執(zhí)行%)降狠。相等運算符==優(yōu)先級高于邏輯與運算符&&否纬。該優(yōu)先級意味著以下兩行代碼以相同的方式執(zhí)行:
// 使用括號提高可讀性
if ((n % i == 0) && (d % i == 0)) ...
// 比較難讀临燃,但跟上面是相等的
if (n % i == 0 && d % i == 0) ...
算數(shù)運算符
Dart支持以下的算數(shù)運算符
運算符 | 含義 |
---|---|
+ | 加法 |
- | 相減 |
-expr | 取反 |
* | 相乘 |
/ | 相除,返回的是double |
~/ | 相除,返回的是整數(shù)int |
% | 取余 |
比如:
assert(2 + 3 == 5);
assert(2 - 3 == -1);
assert(2 * 3 == 6);
assert(5 / 2 == 2.5); // Result is a double
assert(5 ~/ 2 == 2); // Result is an int
assert(5 % 2 == 1); // Remainder
assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');
Dart還支持自增自減運算:
var a, b;
a = 0;
b = ++a; // Increment a before b gets its value.
assert(a == b); // 1 == 1
a = 0;
b = a++; // Increment a AFTER b gets its value.
assert(a != b); // 1 != 0
a = 0;
b = --a; // Decrement a before b gets its value.
assert(a == b); // -1 == -1
a = 0;
b = a--; // Decrement a AFTER b gets its value.
assert(a != b); // -1 != 0
相等和關(guān)系運算符
跟其他語言的一樣
類型判斷運算符
as, is, 和 is! 操作符在運行時檢查類型非常方便。
操作符 | 含義 |
---|---|
as | 類型轉(zhuǎn)換(也用于指定庫前綴) |
is | 如果對象具有指定的類型則返回true |
is! | 如果對象具有指定的類型返回false |
如果obj
實現(xiàn)了由T
指定的接口,obj is T
的結(jié)果為true薄货。比如,obj is Object
永遠都是true赊瞬。
使用as
運算符將對象強制轉(zhuǎn)換為特定的類型巧涧。 通常情況下應(yīng)該將as
作為is
的簡寫,比如:
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
可以使用as
來簡寫:
(emp as Person).firstName = 'Bob';
注意: 該代碼并不是等價的缩筛。如果 emp是null或者不是Person類的對象。使用is不會有什么影響桐臊,使用as的話會拋出異常伤提。
賦值運算符
// 將value賦值給a
a = value;
// 如果b是null肿男,將value賦值給b;否則冠王,b將保持不變
b ??= value;
邏輯運算符
跟其他語言類似
位運算
與C一樣
final value = 0x22;
final bitmask = 0x0f;
assert((value & bitmask) == 0x02); // AND
assert((value & ~bitmask) == 0x20); // AND NOT
assert((value | bitmask) == 0x2f); // OR
assert((value ^ bitmask) == 0x2d); // XOR
assert((value << 4) == 0x220); // Shift left
assert((value >> 4) == 0x02); // Shift right
條件表達式
Dart有兩個運算符豪娜,可以簡明地計算可能需要if-else語句的表達式:
condition ? expr1 : expr2
三目運算符瘤载,跟其他語言一樣
expr1 ?? expr2
如果expr1是 non-null,則返回它的值墨技,否則,計算并返回expr2的值崭别。
如果要基于布爾表達式來賦值的話使用三目運算
var visibility = isPublic ? 'public' : 'private';
如果布爾表達式要測試null茅主,請考慮使用??。
String playerName(String name) => name ?? 'Guest';
前面的例子至少可以用其他兩種方式編寫娃善,但不夠簡潔:
// Slightly longer version uses ?: operator.
String playerName(String name) => name != null ? name : 'Guest';
// Very long version uses if-else statement.
String playerName(String name) {
if (name != null) {
return name;
} else {
return 'Guest';
}
級聯(lián)表示法(..
)
級聯(lián)(..
)允許對同一對象進行一系列操作聚磺。 除了函數(shù)調(diào)用,還可以訪問同一對象上的字段焕阿。 這通常可以節(jié)省創(chuàng)建臨時變量的步驟褒纲,并允許編寫更多流暢的代碼。
querySelector('#confirm') // Get an object.
..text = 'Confirm' // Use its members.
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
上面的代碼等同于:
var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));
也可以內(nèi)嵌我們的級聯(lián)表達式,比如:
final addressBook = (AddressBookBuilder()
..name = 'jenny'
..email = 'jenny@example.com'
..phone = (PhoneNumberBuilder()
..number = '415-555-0100'
..label = 'home')
.build())
.build();
小心在返回實際對象的函數(shù)上構(gòu)造級聯(lián)唇兑。 例如,以下代碼會失斉撩蕖:
var sb = StringBuffer();
sb.write('foo')
..write('bar'); // Error: method 'write' isn't defined for 'void'.
sb.write()
函數(shù)返回void,不能在void上構(gòu)建級聯(lián)即纲。
注意:嚴格來說蜂厅,級聯(lián)的“雙點”符號不是運算符。 它只是Dart語法的一部分稠通。
其他運算符
只介紹下?.
:最左邊的操作數(shù)可以為null,比如:foo?.bar
,如果foo不為null飞主,則從foo中選擇bar屬性,如果foo為null丸冕,則foo?.bar
為null眼姐。
控制流語句
if-else,for循環(huán),while/do-while跟其他語言一樣
Dart中的Switch語句使用==來比較整數(shù)罢杉,字符串或者編譯時常量。比較對象必須是同一個類的實例(而不是其子類)律想,并且該類不能覆蓋==。
每個非空case子句以break語句結(jié)束而叼。 結(jié)束非空case子句的其他有效方法是continue液荸,throw或return語句。
var command = 'OPEN';
switch (command) {
case 'CLOSED':
executeClosed();
break;
case 'PENDING':
executePending();
break;
case 'APPROVED':
executeApproved();
break;
case 'DENIED':
executeDenied();
break;
case 'OPEN':
executeOpen();
break;
default:
executeUnknown();
}
斷言
跟其他語言的斷言一樣。Flutter中只有在debug模式下才開啟斷言细疚。
異常
與Java相比,Dart的所有異常都是未經(jīng)檢查的異常吧彪。 方法不會聲明它們可能引發(fā)的異常,并且不需要捕獲任何異常傀缩。
Dart提供了Exception和Error類型,以及許多預(yù)定義的子類型。 當然料身,也可以自定義異常。 Dart程序可以拋出任何非null對象(不僅僅是Exception和Error對象)作為異常隙畜。
Throw
拋出異常:
throw FormatException('Expected at least 1 section');
也可以拋出任意對象:
throw 'Out of llamas!';
Catch
捕獲異常會阻止異常傳播(除非重新拋出異常),并有機會處理它:
try {
breedMoreLlamas();
} on OutOfLlamasException {
buyMoreLlamas();
}
要處理可能拋出多種類型異常的代碼,可以指定多個catch子句言询。 與拋出對象的類型匹配的第一個catch子句處理異常。 如果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) {
// No specified type, handles all
print('Something really unknown: $e');
}
如前面的代碼所示,可以使用on
或catch
或兩者結(jié)合使用腕巡。 需要指定異常類型時使用on
, 在需要異常對象時使用catch
。
可以指定兩個參數(shù)到catch()
转质。第一個參數(shù)是拋出的異常沸枯,第二個是堆棧跟蹤信息(一個StackTrace
對象)。
try {
// ···
} on Exception catch (e) {
print('Exception details:\n $e');
} catch (e, s) {
print('Exception details:\n $e');
print('Stack trace:\n $s');
}
要部分處理異常翔怎,同時允許異常傳播,可以使用rethrow
關(guān)鍵字宣脉。
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}.');
}
}
Finally
為了確保無論異常是否拋出都會執(zhí)行一些代碼谈跛,可以使用finally
語句感憾。如果catch
語句沒有匹配到該異常巍虫,則該異常會在執(zhí)行finally
語句的代碼之后拋出。
try {
breedMoreLlamas();
} finally {
// Always clean up, even if an exception is thrown.
cleanLlamaStalls();
}
finally
語句會在匹配異常的catch
語句之后執(zhí)行:
try {
breedMoreLlamas();
} catch (e) {
print('Error: $e'); // Handle the exception first.
} finally {
cleanLlamaStalls(); // Then clean up.
}
類
Dart是一種面向?qū)ο蟮恼Z言,具有類和基于mixin的繼承。 每個對象都是一個類的實例负芋,所有類都來自O(shè)bject旧蛾。 基于Mixin的繼承意味著雖然每個類(除了Object)只有一個超類,但是類的body可以在多個類層次結(jié)構(gòu)中重用蠕嫁。
類的成員
類具有函數(shù)锨天、方法以及實例變量等成員。
使用點語法(.
)來引用實例變量或者方法:
var p = Point(2, 2);
// Set the value of the instance variable y.
p.y = 3;
// Get the value of y.
assert(p.y == 3);
// Invoke distanceTo() on p.
num distance = p.distanceTo(Point(4, 4));
使用?.
來代替.
剃毒,可以防止左邊運算對象為null的異常搂赋。
// If p is non-null, set its y value to 4.
p?.y = 4;
使用構(gòu)造函數(shù)
可以使用構(gòu)造函數(shù)創(chuàng)建對象。 構(gòu)造函數(shù)名稱可以是ClassName
或ClassName.identifier
益缠。 例如厂镇,以下代碼使用Point()
和Point.fromJson()
構(gòu)造函數(shù)創(chuàng)建Point
對象:
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
一些類提供了編譯時構(gòu)造函數(shù),創(chuàng)建一個編譯時常量左刽。在構(gòu)造函數(shù)名稱前加const
關(guān)鍵字:
var p = const ImmutablePoint(2, 2);
構(gòu)造兩個相同的編譯時常量時捺信,只會產(chǎn)生一個實例:
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
assert(identical(a, b)); // a和b是同一個實例
在常量上下文中,可以在構(gòu)造函數(shù)或字面量之前省略const
:
// Lots of const keywords here.
const pointAndLine = const {
'point': const [const ImmutablePoint(0, 0)],
'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
除了第一個const
,其他的都可以省略:
// 只有一個const欠痴,它建立了恒定的上下文迄靠。
const pointAndLine = {
'point': [ImmutablePoint(0, 0)],
'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};
如果常量構(gòu)造函數(shù)在常量上下文之外,并且在沒有使用const
,則會創(chuàng)建一個非常量對象:
var a = const ImmutablePoint(1, 1); // Creates a constant
var b = ImmutablePoint(1, 1); // Does NOT create a constant
assert(!identical(a, b)); // NOT the same instance!
獲取對象類型
在運行時獲取對象的類型喇辽,可以使用對象的runtimeType
屬性掌挚,返回一個Type對象/
print('The type of a is ${a.runtimeType}');
實例變量
聲明實例變量:
class Point {
num x; // Declare instance variable x, initially null.
num y; // Declare y, initially null.
num z = 0; // Declare z, initially 0.
}
所有未初始化的實例變量默認值都是null
.
所有的實例變量都會生成一個隱式的getter方法。非final
實例變量也會隱式的生成setter方法菩咨。
class Point {
num x;
num y;
}
void main() {
var point = Point();
point.x = 4; // Use the setter method for x.
assert(point.x == 4); // Use the getter method for x.
assert(point.y == null); // Values default to null.
}
構(gòu)造函數(shù)
通過創(chuàng)建與其類同名的函數(shù)來聲明構(gòu)造函數(shù)吠式。
class Point {
num x, y;
Point(num x, num y) {
// There's a better way to do this, stay tuned.
this.x = x;
this.y = y;
}
}
this
關(guān)鍵字代表著當前實例。在名稱沖突時使用this
,一般情況下抽米,Dart會省略this
.
Dart具有語法糖特占,使其變得簡單:
class Point {
num x, y;
// Syntactic sugar for setting x and y
// before the constructor body runs.
Point(this.x, this.y);
}
默認構(gòu)造函數(shù)
如果沒有聲明構(gòu)造函數(shù),則會提供一個默認的構(gòu)造函數(shù)云茸。默認的構(gòu)造函數(shù)沒有參數(shù)是目,并且會調(diào)用其父類的無參數(shù)的構(gòu)造函數(shù)。
構(gòu)造函數(shù)不能繼承
子類不能繼承父類的構(gòu)造函數(shù)标捺!
命名構(gòu)造函數(shù)
使用命名構(gòu)造函數(shù)為類實現(xiàn)多個構(gòu)造函數(shù):
class Point {
num x, y;
Point(this.x, this.y);
// Named constructor
Point.origin() {
x = 0;
y = 0;
}
}
重定向構(gòu)造函數(shù)
有時構(gòu)造函數(shù)的唯一目的是重定向到同一個類中的另一個構(gòu)造函數(shù)懊纳。 重定向構(gòu)造函數(shù)的body是空的,構(gòu)造函數(shù)調(diào)用出現(xiàn)在冒號(:
)之后亡容。
class Point {
num x, y;
// The main constructor for this class.
Point(this.x, this.y);
// Delegates to the main constructor.
// 重定向到main函數(shù)
Point.alongXAxis(num x) : this(x, 0);
}
常量構(gòu)造函數(shù)
如果類生成的對象永遠不會改變嗤疯,則可以使這些變量為編譯時常量。定義一個const
構(gòu)造函數(shù)來確保所有的實例變量都是final
闺兢。
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
工廠(Factory)構(gòu)造函數(shù)
當構(gòu)造函數(shù)不需要每次都創(chuàng)建新的實例時茂缚,可以使用factory
關(guān)鍵字。例如列敲,一個工廠構(gòu)造函數(shù)可能從緩存中返回實例阱佛,或者可能返回一個子類的實例。
下面的例子展示了工廠構(gòu)造函數(shù)從緩存中返回實例:
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 = Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
注意: 工廠構(gòu)造函數(shù)不能訪問
this
.
調(diào)用工廠構(gòu)造函數(shù)跟其他的構(gòu)造函數(shù)一樣:
var logger = Logger('UI');
logger.log('Button clicked');
方法
方法是為對象提供行為的函數(shù)戴而。(函數(shù)是獨立存在的,方法需要依賴對象翩蘸,這就是函數(shù)與方法的區(qū)別)所意。
實例方法
實例方法可以訪問實例變量和this
。下面的distanceTo()
就是一個實例方法:
import 'dart:math';
class Point {
num x, y;
Point(this.x, this.y);
num distanceTo(Point other) {
var dx = x - other.x;
var dy = y - other.y;
return sqrt(dx * dx + dy * dy);
}
}
getter & setter
每個實例變量都有一個隱式的getter,合適的話還有一個setter扶踊⌒古簦可以通過set
和get
關(guān)鍵字實現(xiàn)setter和getter來創(chuàng)建其他的屬性
class Rectangle {
num left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
// 定義兩個計算屬性: 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;
}
void main() {
var rect = Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;
assert(rect.left == -8);
}
抽象方法
實例方法,setter和getter可以是抽象的秧耗,可以定義接口备籽,但將其實現(xiàn)留給其他類。抽象方法只能存在于抽象類分井。
使用分號(;
)而不是方法體來定義一個抽象方法:
abstract class Doer {
// Define instance variables and methods...
void doSomething(); // 定義抽象方法
}
class EffectiveDoer extends Doer {
void doSomething() {
// Provide an implementation, so the method is not abstract here...
}
}
抽象類
使用abstract
修飾符來定義抽象類(無法實例化的類)车猬。抽象類對于定義接口非常有用,通常還有一些實現(xiàn)尺锚。如果希望抽象類看起來是可以實例化的珠闰,請定義工廠構(gòu)造函數(shù)。
抽象類一般具有抽象方法:
// 這個類被定義為抽象類瘫辩,因此它不能實例化
abstract class AbstractContainer {
// 定義構(gòu)造函數(shù)伏嗜,字段,方法...
void updateChildren(); //抽象方法
}
隱式接口
每個類都隱式定義一個接口伐厌,該接口包含該類的所有實例成員及其實現(xiàn)的所有接口承绸。如果要在不繼承class B實現(xiàn)的情況下,創(chuàng)建一個class A來支持class B的API挣轨,class A應(yīng)該implements
B的接口八酒。
// A person. The implicit interface contains 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.';
}
// An implementation of the Person interface.
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()));
}
下面是一個類實現(xiàn)多個接口的例子:
class Point implements Comparable, Location {...}
擴展類
使用extends
創(chuàng)建子類,使用super
引用父類:
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ···
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ···
}
override 成員
子類可以覆蓋實例方法刃唐、getter
和setter
羞迷。可以使用@override
注釋來表示要覆蓋一個成員:
class SmartTelevision extends Television {
@override
void turnOn() {...}
// ···
}
覆蓋運算符
您可以覆蓋下表中顯示的操作符画饥。例如衔瓮,如果您定義一個向量類,您可能定義一個+方法來添加兩個向量抖甘。
> |
/ |
^ |
[]= |
---|---|---|---|
< |
+ |
| |
[] |
<= |
~/ |
& |
~ |
>= |
* |
<< |
== |
- |
% |
>> |
注意:
!=
是不能覆蓋的热鞍,因為e1 != e2
是!(e1 == e2)
的語法糖。
下面是一個覆蓋+
和 -
操作符的例子:
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));
}
如果你覆蓋了==
,你也應(yīng)該覆蓋對象的hashCode
getter方法衔彻。
noSuchMethod()
當代碼試圖調(diào)用不存在的方法或者實例變量薇宠,可以覆蓋noSuchMethod()
來檢測或響應(yīng)。
class A {
// 除非覆蓋了noSuchMethod艰额。使用不存在的成員會導(dǎo)致NoSuchMethodError
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
枚舉類型
使用
使用enum
關(guān)鍵字聲明一個枚舉類型:
enum Color { red, green, blue }
每個枚舉值都有一個index
getter,它返回枚舉值的位置澄港。比如,第一個值的index
為0柄沮,第二個值的index
為1.
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
要獲取枚舉中所有的值,可以使用枚舉中的values
常量回梧。
List<Color> colors = Color.values;
assert(colors[2] == Color.blue);
枚舉有以下限制:
- 不能子類化废岂、
mixin
或者implment
一個枚舉 - 不能顯式的實例化枚舉
向類添加feature: mixin
mixin是一種在多個繼承類中重用代碼的一種方式。
若要使用mixin狱意,請使用with
關(guān)鍵字后跟一個或多個mixin名稱湖苞。下面的例子顯示了兩個使用mixin的類:
class Musician extends Performer with Musical {
// ···
}
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
要實現(xiàn)mixin
,創(chuàng)建一個擴展Object
的類详囤,并且不聲明構(gòu)造函數(shù)财骨,除非希望mixin
像常規(guī)類一樣使用。
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');
}
}
}
比如藏姐,指定只有某些類型可以使用mixin隆箩,這樣你的mixin可以調(diào)用它沒有定義的方法-使用on
來指定所需的父類:
mixin MusicalPerformer on Musician {
// ···
}
Dart 2.1版本中引入了對mixin的支持,早期版本中通常使用抽象類
類變量和方法
使用static
關(guān)鍵字來實現(xiàn)類范圍的變量和方法包各。
靜態(tài)變量(Static變量)
靜態(tài)變量(類變量)對于類范圍內(nèi)的狀態(tài)和常量很有用:
class Queue {
static const initialCapacity = 16;
// ···
}
void main() {
assert(Queue.initialCapacity == 16);
}
靜態(tài)變量在使用之前不會初始化
靜態(tài)方法
靜態(tài)方法(類方法)不能操作實例摘仅,因為不能訪問this
:
import 'dart:math';
class Point {
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(2.8 < distance && distance < 2.9);
print(distance);
}
可以使用靜態(tài)方法作為編譯時常量,比如问畅,可以傳遞一個靜態(tài)方法作為靜態(tài)構(gòu)造函數(shù)的參數(shù)
注意: 對于通用的或者廣泛使用的功能函數(shù)娃属,考慮使用頂層函數(shù),而不是靜態(tài)方法
泛型
如果查看List的API文檔护姆,會發(fā)現(xiàn)List的實際類型是List<E>
矾端。<...>
表示法將List標記為泛型(或者參數(shù)化)類型-具有正式類型參數(shù)的類型。按照慣例卵皂,大多數(shù)類型變量都有單字母名稱秩铆,例如E,T灯变,S殴玛,K,V
為什么使用泛型
類型安全通常需要泛型添祸,但有更多的好處:
- 正確指定泛型類型會產(chǎn)生更好的代碼
- 使用泛型來減少代碼重復(fù)
如果想要數(shù)組僅僅包含字符串滚粟,可以聲明為List<String>
。這樣可非字符串插入到列表中的就會有錯誤:
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // Error
使用泛型的另外一個理由是減少代碼的重復(fù)刃泌。泛型允許在多個類型之間分享單個接口和實現(xiàn)凡壤。比如:創(chuàng)建一個用于緩存對象的接口:
abstract class ObjectCache {
Object getByKey(String key);
void setByKey(String key, Object value);
}
然后發(fā)現(xiàn)想要一個特定于字符串的版本,然后可以這樣實現(xiàn):
abstract class StringCache {
String getByKey(String key);
void setByKey(String key, String value);
}
后來耙替,你可能需要更多的類型...
泛型可以省去所有這些接口的麻煩亚侠,創(chuàng)建一個帶有類型參數(shù)的接口:
abstract class Cache<T> {
T getByKey(String key);
void setByKey(String key, T value);
}
在這段代碼中,T
是一個替身類型俗扇,一個占位符硝烂,
使用集合字面量
List
,Set
,Map
可以參數(shù)化:
var names = <String>['Seth', 'Kathy', 'Lars'];
var uniqueNames = <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ù)化類型
要在使用構(gòu)造函數(shù)時指定一個或多個類型,請將類型放在類名后面的尖括號中(<...>
),比如:
var nameSet = Set<String>.from(names);
var views = Map<int, View>();
泛型集合以及其所包含的類型
Dart泛型被具體化狐援,這意味著它們在運行時攜帶類型信息钢坦。例如究孕,你可以測試一個集合的類型:
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
限制參數(shù)化類型
當實現(xiàn)一個泛型時啥酱,可能想要限制參數(shù)的類型爹凹,可以使用extends
:
class Foo<T extends SomeBaseClass> {
// Implementation goes here...
String toString() => "Instance of 'Foo<$T>'";
}
class Extender extends SomeBaseClass {...}
使用SomeBaseClass
或其子類作為泛型參數(shù)是OK的:
var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();
不指定泛型參數(shù)也是可以的:
var foo = Foo();
print(foo); // Instance of 'Foo<SomeBaseClass>'
指定任意非SomeBaseClass
類型會導(dǎo)致錯誤:
var foo = Foo<Object>();
使用泛型方法
T first<T>(List<T> ts) {
// Do some initial work or error checking, then...
T tmp = ts[0];
// Do some additional checking or processing...
return tmp;
}
first(<T>)
上的泛型類型參數(shù)允許在幾個地方使用類型參數(shù)T:
- 函數(shù)的返回類型(
T
) - 參數(shù)的類型(
List<T>
) - 局部變量(
T tmp
)
支持異步
Dart庫充滿了返回Future
或者Stream
對象的函數(shù)。這些函數(shù)是異步的:它們在一個可能非常耗時的操作(比如I/O)之后返回镶殷,而不需要等待操作完成禾酱。
async
和wait
關(guān)鍵字支持異步編程,允許我們編寫看起來類似同步的異步代碼
處理Future
當需要一個完整Future的結(jié)果時绘趋,有兩種選擇:
- 使用
async
和await
- 使用 Future API
使用async
和await
的代碼是異步的颤陶,但看起來是同步的。比如:
await lookUpVersion();
要使用await
陷遮,代碼必須在async
函數(shù)中:一個標記為async
的函數(shù):
Future checkVersion() async {
var version = await lookUpVersion();
// Do something with version
}
注意:雖然
async
函數(shù)可能會執(zhí)行耗時操作滓走,但并不需要等待這些操作。相反帽馋,async
函數(shù)只執(zhí)行到第一個await
表達式搅方,然后返回一個Future
對象,僅在await
表達式完成后才恢復(fù)執(zhí)行绽族。
使用try
姨涡,catch
,和 finally
在使用await
的代碼中來錯誤:
try {
version = await lookUpVersion();
} catch (e) {
// React to inability to look up the version
}
可以在async
函數(shù)中多次使用await
:
var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
在await
表達式中,表達式的值通常是Future
吧慢,如果不是涛漂,該值也會自動包裝在Future中,await
表達式會使執(zhí)行暫停检诗,知道該對象可用匈仗。
如果在使用await
時遇到編譯時錯誤,請確保await
是在async
函數(shù)中逢慌。
比如在app的main()
函數(shù)中使用await
悠轩,則必須時main函數(shù)標記為aycnc
Future main() async {
checkVersion();
print('In main: version is ${await lookUpVersion()}');
}
處理Stream(流)
當需要從流中獲取值的時候,有兩個選項:
- 使用
async
和異步for循環(huán)(await for
) - 使用 Stream API
異步for循環(huán):
await for (varOrType identifier in expression) {
// Executes each time the stream emits a value.
}
表達式(expression)的值必須具有Stream
類型涕癣。執(zhí)行過程如下:
- 等流發(fā)出一個值
- 執(zhí)行for循環(huán)的主體哗蜈,將變量設(shè)置為流發(fā)出的值
- 重復(fù)1和2,直到流關(guān)閉
停止監(jiān)聽流坠韩,可以使用break
或者return
語句距潘,該語句會中斷for循環(huán)并且取消訂閱流
如果實現(xiàn)一個異步for循環(huán)時出現(xiàn)編譯時錯誤,確保await for
在異步函數(shù)中
Future main() async {
// ...
await for (var request in requestServer) {
handleRequest(request);
}
// ...
}
Generators(生成器)
當想要懶加載一系列值時只搁,可以考慮使用generator函數(shù)音比,dart內(nèi)置兩種該函數(shù):
- 同步生成器:返回
Iterable
對象 - 異步生成器:返回
Stream
對象
要實現(xiàn)同步生成器函數(shù),將函數(shù)主體標記為sync*
氢惋,并使用yield
語句來傳遞值:
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
要實現(xiàn)異步生成器函數(shù)洞翩,將函數(shù)標記為async*
稽犁,并使用yield
語句來傳遞值:
Stream<int> asynchronousNaturalsTo(int n) async* {
int k = 0;
while (k < n) yield k++;
}
如果生成器是遞歸的,可以使用yield*
來提高性能:
Iterable<int> naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
}
}
Isolates(隔離區(qū))
大多數(shù)計算機骚亿,即使在移動平臺上已亥,也有多核CPU。 為了利用所有這些核心来屠,開發(fā)人員傳統(tǒng)上使用并發(fā)運行的共享內(nèi)存線程虑椎。 但是,共享狀態(tài)并發(fā)容易出錯俱笛,并且可能導(dǎo)致代碼復(fù)雜化捆姜。
所有Dart代碼都在隔離區(qū)內(nèi)運行,而不是線程迎膜。 每個隔離區(qū)都有自己的內(nèi)存堆泥技,確保不會從任何其他隔離區(qū)訪問隔離區(qū)的狀態(tài)。