Dart基本語法

重要概念

  • 可以放在變量中的都是對象,所有對象都是類的實例,包括數(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上偶垮,值的范圍:-2^{63}\2^{63}-1张咳。 編譯為JavaScript的Dart使用的是Javascript number,值的范圍是 -2^{53}2^{53}-1

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 ifcollection 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信息。codeUnitAtcodeUnit屬性返回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');
}

如前面的代碼所示,可以使用oncatch或兩者結(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ù)名稱可以是ClassNameClassName.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扶踊⌒古簦可以通過setget關(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)該implementsB的接口八酒。

// 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 成員

子類可以覆蓋實例方法刃唐、gettersetter羞迷。可以使用@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)該覆蓋對象的hashCodegetter方法衔彻。

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)之后返回镶殷,而不需要等待操作完成禾酱。

asyncwait關(guān)鍵字支持異步編程,允許我們編寫看起來類似同步的異步代碼

處理Future

當需要一個完整Future的結(jié)果時绘趋,有兩種選擇:

使用asyncawait的代碼是異步的颤陶,但看起來是同步的。比如:

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í)行過程如下:

  1. 等流發(fā)出一個值
  2. 執(zhí)行for循環(huán)的主體哗蜈,將變量設(shè)置為流發(fā)出的值
  3. 重復(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)。


文章來源:https://dart.dev/guides/language/language-tour

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末磕仅,一起剝皮案震驚了整個濱河市珊豹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宽涌,老刑警劉巖平夜,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異卸亮,居然都是意外死亡忽妒,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門兼贸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來段直,“玉大人,你說我怎么就攤上這事溶诞⊙烀剩” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵螺垢,是天一觀的道長喧务。 經(jīng)常有香客問我,道長枉圃,這世上最難降的妖魔是什么功茴? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮孽亲,結(jié)果婚禮上坎穿,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好玲昧,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布栖茉。 她就那樣靜靜地躺著,像睡著了一般孵延。 火紅的嫁衣襯著肌膚如雪吕漂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天隙袁,我揣著相機與錄音痰娱,去河邊找鬼弃榨。 笑死菩收,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的鲸睛。 我是一名探鬼主播娜饵,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼官辈!你這毒婦竟也來了肝劲?” 一聲冷哼從身側(cè)響起参歹,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后恶座,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡添瓷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年每庆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹅经。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡寂呛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瘾晃,到底是詐尸還是另有隱情贷痪,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布蹦误,位于F島的核電站劫拢,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏强胰。R本人自食惡果不足惜舱沧,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望哪廓。 院中可真熱鬧狗唉,春花似錦、人聲如沸涡真。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至缸剪,卻和暖如春吗铐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背杏节。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工唬渗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奋渔。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓镊逝,卻偏偏與公主長得像,于是被迫代替她去往敵國和親嫉鲸。 傳聞我的和親對象是個殘疾皇子撑蒜,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容