1. 程式人生 > >flutter--Dart基礎語法(一)

flutter--Dart基礎語法(一)

一、前言

Flutter 是 Google 開源的 UI 工具包,幫助開發者通過一套程式碼庫高效構建多平臺精美應用,Flutter 開源、免費,擁有寬鬆的開源協議,支援移動、Web、桌面和嵌入式平臺。

Flutter是使用Dart語言開發的跨平臺移動UI框架,通過自建繪製引擎,能高效能、高保真地進行Android和IOS開發。Flutter採用Dart語言進行開發,而並非Java,Javascript這類熱門語言,這是Flutter團隊對當前熱門的10多種語言慎重評估後的選擇。因為Dart囊括了多數程式語言的優點,它更符合Flutter構建介面的方式。

本文主要就是簡單梳理一下Dart語言的一些基礎知識和語法。關於程式語言的基本語法無外乎那麼些內容,註釋、變數、資料型別、運算子、流程控制、函式、類、異常、檔案、非同步、常用庫等內容,相信大部分讀者都是有一定程式設計基礎的,所以本文就簡單地進行一個梳理,不做詳細的講解。大家也可以參考 Dart程式語言中文網。

二、Dart的基本語法

Dart基本語法是指編寫dart程式碼最基本的一些內容、規範,主要包括註釋、變數、資料型別和運算子等內容。

2.1 註釋

Dart 支援單行註釋、多行註釋和文件註釋。

  • 單行註釋:單行註釋以 // 開始。 所有在 // 和改行結尾之間的內容被編譯器忽略。
    void main() {
      // TODO: refactor into an AbstractLlamaGreetingFactory?
      print('Welcome to my Llama farm!');
    }
  • 多行註釋:多行註釋以  /* 開始, 以 */
     結尾。 所有在 /* 和 */ 之間的內容被編譯器忽略 (不會忽略文件註釋)。 多行註釋可以巢狀。
    void main() {
      /*
       * This is a lot of work. Consider raising chickens.
    
      Llama larry = Llama();
      larry.feed();
      larry.exercise();
      larry.clean();
       */
    }
  • 文件註釋:文件註釋可以是多行註釋,也可以是單行註釋, 文件註釋以 /// 或者 /** 開始。 在連續行上使用 ///
     與多行文件註釋具有相同的效果。在文件註釋中,除非用中括號括起來,否則Dart 編譯器會忽略所有文字。 使用中括號可以引用類、 方法、 欄位、 頂級變數、 函式、 和引數。 括號中的符號會在已記錄的程式元素的詞法域中進行解析。下面是一個引用其他類和成員的文件註釋,在生成的文件中,[Food] 會成為一個連結, 指向 Food 類的 API 文件。
    /// A domesticated South American camelid (Lama glama).
    ///
    /// 自從西班牙時代以來,
    /// 安第斯文化就將駱駝當做肉食類和運輸類動物。
    class Llama {
      String name;
    
      /// 餵養駱駝 [Food].
      ///
      /// 典型的美洲駝每週吃一捆乾草。
      void feed(Food food) {
        // ...
      }
    
      /// 使用 [activity] 訓練駱駝
      /// [timeLimit] 分鐘。
      void exercise(Activity activity, int timeLimit) {
        // ...
      }
    }

2.2 變數

任何儲存在變數中的都是一個 物件 , 並且所有的物件都是對應一個  的例項。 無論是數字,函式和 null都是物件。所有物件繼承自Object 類。儘管 Dart 是強型別的,但是 Dart 可以推斷型別,所以型別註釋是可選的。 如果要明確說明不需要任何型別, 需要使用特殊型別 dynamic 。

2.2.1 建立變數

var name = 'Bob';

變數僅儲存物件引用,這裡的變數是 name 儲存了一個 String 型別的物件引用。 “Bob” 是這個 String 型別物件的值。

name 變數的型別被推斷為 String 。 但是也可以通過指定型別的方式,來改變變數型別。 如果物件不限定為單個型別,可以指定為 物件型別 或 動態型別

//指定為動態型別
dynamic name = 'Bob';
//顯示指定為字串型別
String name = 'Bob';

2.2.2 預設值

未初始化的變數預設值是 null。即使變數是數字 型別預設值也是 null,因為在 Dart 中一切都是物件,數字型別 也不例外。

int lineCount;
assert(lineCount == null); //結果為true 

提示: 在生產環境程式碼中 assert() 函式會被忽略,不會被呼叫。 在開發過程中, assert(condition) 會在非 true 的條件下丟擲異常。

2.3 常量 Final 和 Const

使用過程中從來不會被修改的值,我們成為常量,可以使用 final 或 const, 而不是 var 或者其他型別。 Final 變數的值只能被設定一次; Const 變數在編譯時就已經固定 (Const 變數 是隱式 Final 的型別.) 。最高階 final 變數或類變數在第一次使用時被初始化。

提示: 例項變數可以是 final 型別但不能是 const 型別。 必須在建構函式體執行之前初始化 final 例項變數 —— 在變數宣告中,引數建構函式中或建構函式的初始化列表中進行初始化。

2.2.1 常量的建立

final name = 'Bob'; // Without a type annotation
final String nickname = 'Bobby';
//final 不能被修改:
name = 'Alice'; // Error: 一個 final 變數只能被設定一次。

如果需要在編譯時就固定變數的值,可以使用 const 型別變數。 如果 Const 變數是類級別的,需要標記為 static const。 在這些地方可以使用在編譯時就已經固定不變的值,字面量的數字和字串, 固定的變數,或者是用於計算的固定數字:

const bar = 1000000; // 壓力單位 (dynes/cm2)
const double atm = 1.01325 * bar; // 標準氣壓

// Const 關鍵字不僅可以用於宣告常量變數。 還可以用來建立常量值,以及宣告建立常量值的建構函式。 任何變數都可以擁有常量值。
var foo = const [];
final bar = const []; 
const baz = []; // 宣告 const 的初始化表示式中 const 可以被省略。 比如上面的 baz。  Equivalent to `const []`

//Const 變數的值不可以修改:
baz = [42]; // Error: 常量變數不能賦值修改。

//非 Final , 非 const 的變數是可以被修改的,即使這些變數 曾經引用過 const 值。
foo = [1, 2, 3]; // 曾經引用過 const [] 常量值。

2.2.2 final和const的相同點

1.宣告時必須要賦值

  

2.只能在初始化賦值一次,之後不能重新賦值

3.後面都不能接var關鍵字

4.型別宣告可以忽略,類似 var,可以根據初始化的值推斷出變數型別

2.2.3 final和const的區別

1、final變數的初始值可以在編譯時確定,也可以在執行時確定,cosnt變數的初始值只能是編譯時確定的值,比如當前時間

2.const變數的不可變性是巢狀的,final不是

const a = {'c': 1};
a['c'] = 2;
// 執行結果 Unsupported operation: Cannot set value in unmodifiable Map

3.記憶體中的建立:相同的值,final變數會重複建立,const會引用同一份值

  const a = {'c': 1};
  const b = {'c': 1};
  print(a == b);//true
  final c = {'c': 1};
  final d = {'c': 1};
  print(c == d);//false

2.4 資料型別

Dart 語言支援以下內建型別:

  • Number:數值型別
  • String:字串型別
  • Boolean:布林型別
  • List (也被稱為 Array):列表或陣列型別
  • Map:字典型別
  • Set:集合型別
  • Rune (用於在字串中表示 Unicode 字元):
  • Symbol:符號型別

這些型別都可以被初始化為字面量。 例如, 'this is a string' 是一個字串的字面量, true 是一個布林的字面量。因為在 Dart 所有的變數終究是一個物件(一個類的例項), 所以變數可以使用 構造涵數 進行初始化。 一些內建型別擁有自己的建構函式。 例如, 通過 Map() 來構造一個 map 變數。

2.4.1 Number

Dart 語言的 Number 有兩種型別:

  • int:整數值不大於64位, 具體取決於平臺。 在 Dart VM 上, 值的範圍從 -263到 263 - 1. Dart 被編譯為 JavaScript 時,使用 JavaScript numbers, 值的範圍從 -253 到 253 - 1.
  • double:64位(雙精度)浮點數,依據 IEEE 754 標準。

int 和 double 都是 num. 的亞型別。 num 型別包括基本運算 +, -, /, 和 *, 以及 abs() ceil(), 和 floor(), 等函式方法。 (按位運算子,例如»,定義在 int 類中。) 如果 num 及其亞型別找不到你想要的方法, 嘗試查詢使用 dart:math 庫。

// 整數型別不包含小數點。 下面是定義整數型別字面量的例子:
var x = 1;
var hex = 0xDEADBEEF;

// 如果一個數字包含小數點,那麼就是小數型別。 下面是定義小數型別字面量的例子: var y = 1.1; var exponents = 1.42e5;
// 從 Dart 2.1 開始,必要的時候 int 字面量會自動轉換成 double 型別。 double z = 1; // 相當於 double z = 1.0. //版本提示: 在dart 2.1 之前,在 double 上下文中使用 int 字面量是錯誤的。 //以下是將字串轉換為數字的方法,反之亦然: // 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');
//int 特有的傳統按位運算操作,移位(<<, >>),按位與(&)以及 按位或(|)。 例如: assert((3 << 1) == 6); // 0011 << 1 == 0110 assert((3 >> 1) == 1); // 0011 >> 1 == 0001 assert((3 | 4) == 7); // 0011 | 0100 == 0111

//數字型別字面量是編譯時常量。 在算術表示式中,只要參與計算的因子是編譯時常量, 那麼算術表示式的結果也是編譯時常量。 const msPerSecond = 1000; const secondsUntilRetry = 5; const msUntilRetry = secondsUntilRetry * msPerSecond;

2.4.2 String

Dart 字串是一組 UTF-16 單元序列。 字串通過單引號或者雙引號建立。

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} 的方式內嵌表示式。 如果表示式是一個識別符號,則 {} 可以省略。 在 Dart 中通過呼叫就物件的 toString() 方法來得到物件相應的字串。

var s = 'string interpolation';

assert('Dart has $s, which is very handy.' ==
    'Dart has string interpolation, ' +
        'which is very handy.');
assert('That deserves all caps. ' +
        '${s.toUpperCase()} is very handy!' ==
    'That deserves all caps. ' +
        'STRING INTERPOLATION is very handy!');

提示: == 運算子用來測試兩個物件是否相等。 在字串中,如果兩個字串包含了相同的編碼序列,那麼這兩個字串相等。

此外,還有字串的拼接和多行字串等用法

// 用 + 運算子來把多個字串連線為一個,也可以把多個字面量字串寫在一起來實現字串連線:
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.'); 


// 使用連續三個單引號或者三個雙引號實現多行字串物件的建立: var s1 = ''' You can create multi-line strings like this one. '''; var s2 = """This is also a multi-line string.""";

// 使用 r 字首,可以建立 “原始 raw” 字串: var s = r"In a raw string, even \n isn't special.";

**一個編譯時常量的字面量字串中,如果存在插值表示式,表示式內容也是編譯時常量, 那麼該字串依舊是編譯時常量。 插入的常量值型別可以是 null,數值,字串或布林值

// const 型別資料
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';

// 非 const 型別資料
var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = [1, 2, 3];

const validConstString = '$aConstNum $aConstBool $aConstString'; //const 型別資料
// const invalidConstString = '$aNum $aBool $aString $aConstList'; //非 const 型別資料,error Not a constant expression.

2.4.3 Boolean

Dart 使用 bool 型別表示布林值。 Dart 只有字面量 true、false 是布林型別, 這兩個物件都是編譯時常量。

Dart 的型別安全意味著不能使用 if (nonbooleanValue) 或者 assert (nonbooleanValue)。 而是應該像下面這樣,明確的進行值檢查:

// 檢查空字串。
var fullName = '';
assert(fullName.isEmpty);

// 檢查 0 值。
var hitPoints = 0;
assert(hitPoints <= 0);

// 檢查 null 值。
var unicorn;
assert(unicorn == null);

// 檢查 NaN 。
var iMeantToDoThis = 0 / 0;
assert(iMeantToDoThis.isNaN);

2.4.4 List

幾乎每種程式語言中最常見的集合可能是 array 或有序的物件集合。 在 Dart 中的 Array 就是 List 物件, 通常稱之為 List 。 下面是一個 Dart List 的示例:

var list = [1, 2, 3];

提示: Dart 推斷 list 的型別為 List<int> 。 如果嘗試將非整數物件新增到此 List 中, 則分析器或執行時會引發錯誤。 

Lists 的下標索引從 0 開始,第一個元素的索引是 0。 list.length - 1 是最後一個元素的索引。 

var list = [1, 2, 3];
assert(list.length == 3);
assert(list[1] == 2);

list[1] = 1;
assert(list[1] == 1);

//在 List 字面量之前新增 const 關鍵字,可以定義 List 型別的編譯時常量
var constantList = const [1, 2, 3];
// constantList[1] = 1; // 取消註釋會引起錯誤。

List 型別包含了很多 List 的操作函式。 更多資訊參考 泛型 和 集合.

2.4.5 Set

在 Dart 中 Set 是一個元素唯一且無序的集合。 Dart 為 Set 提供了 Set 字面量和 Set 型別。

版本提示: 雖然 Set 型別 一直是 Dart 的核心部分, 但在 Dart2.2 中才引入了 Set 字面量 。

下面是通過字面量建立 Set 的一個簡單示例:

var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};

Note: Dart 推斷 halogens 型別為 Set<String> 。如果嘗試為它新增一個 錯誤型別的值,分析器或執行時會丟擲錯誤。

要建立一個空集,使用前面帶有型別引數的 {} ,或者將 {} 賦值給 Set 型別的變數:

var names = <String>{};
// Set<String> names = {}; // 這樣也是可以的。
// var names = {}; // 這樣會建立一個 Map ,而不是 Set 。

是 Set 還是 Map ? Map 字面量語法同 Set 字面量語法非常相似。 因為先有的 Map 字母量語法,所以 {} 預設是 Map 型別。   如果忘記在 {} 上註釋型別或賦值到一個未宣告型別的變數上,   那麼 Dart 會建立一個型別為 Map<dynamic, dynamic> 的物件。

// 使用 add() 或 addAll() 為已有的 Set 新增元素:
var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);

// 使用 .length 來獲取 Set 中元素的個數:
var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);
assert(elements.length == 5);

// 在 Set 字面量前增加 const ,來建立一個編譯時 Set 常量:
final constantSet = const {
  'fluorine',
  'chlorine',
  'bromine',
  'iodine',
  'astatine',
};
// constantSet.add('helium'); // Uncommenting this causes an error.

更多關於 Set 的內容,參閱 Generic 及 Set。

2.4.6 Map

通常來說, Map 是用來關聯 keys 和 values 的物件。 keys 和 values 可以是任何型別的物件。在一個 Map 物件中一個 key 只能出現一次。 但是 value 可以出現多次。 Dart 中 Map 通過 Map 字面量 和 Map 型別來實現。下面是使用 Map 字面量的兩個簡單例子:

var gifts = {
  // Key:    Value
  'first': 'partridge',
  'second': 'turtledoves',
  'fifth': 'golden rings'
};

var nobleGases = {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};

提示: Dart 會將 gifts 的型別推斷為 Map<String, String>, nobleGases 的型別推斷為 Map<int, String> 。 如果嘗試在上面的 map 中新增錯誤型別,那麼分析器或者執行時會引發錯誤。

以上 Map 物件也可以使用 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() ,而不是使用 new Map()。 因為在 Dart 2 中,new 關鍵字是可選的。 

// 新增 key-value 對到已有的 Map 中:
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds'; // Add a key-value pair

// 從一個 Map 中獲取一個 value:
var gifts = {'first': 'partridge'};
assert(gifts['first'] == 'partridge');

// 如果 Map 中不包含所要查詢的 key,那麼 Map 返回 null:
var gifts = {'first': 'partridge'};
assert(gifts['fifth'] == null);

// 使用 .length 函式獲取當前 Map 中的 key-value 對數量:
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds';
assert(gifts.length == 2);

// 建立 Map 型別執行時常量,要在 Map 字面量前加上關鍵字 const。
final constantMap = const {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};

// constantMap[2] = 'Helium'; // 取消註釋會引起錯誤。

更名多關於 Map 的內容,參考 Generics and Maps.

2.4.7 Rune

在 Dart 中, Rune 用來表示字串中的 UTF-32 編碼字元。

Unicode 定義了一個全球的書寫系統編碼, 系統中使用的所有字母,數字和符號都對應唯一的數值編碼。 由於 Dart 字串是一系列 UTF-16 編碼單元, 因此要在字串中表示32位 Unicode 值需要特殊語法支援。

表示 Unicode 編碼的常用方法是, \uXXXX, 這裡 XXXX 是一個4位的16進位制數。 例如,心形符號 (♥) 是 \u2665。 對於特殊的非 4 個數值的情況, 把編碼值放到大括號中即可。 例如,emoji 的笑臉 (�) 是 \u{1f600}

String 類有一些屬性可以獲得 rune 資料。 屬性 codeUnitAt 和 codeUnit 返回16位編碼資料。 屬性 runes 獲取字串中的 Rune 。

下面是示例演示了 Rune 、 16-bit code units、 和 32-bit code points 之間的關係。 

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));
}

提示: 謹慎使用 list 方式操作 Rune 。 這種方法很容易引發崩潰, 具體原因取決於特定的語言,字符集和操作。

2.4.8 Symbol

一個 Symbol 物件表示 Dart 程式中宣告的運算子或者識別符號。 你也許永遠都不需要使用 Symbol ,但要按名稱引用識別符號的 API 時, Symbol 就非常有用了。 因為程式碼壓縮後會改變識別符號的名稱,但不會改變識別符號的符號。 通過字面量 Symbol ,也就是識別符號前面新增一個 # 號,來獲取識別符號的 Symbol 。

#radix
#bar

Symbol 字面量是編譯時常量。

2.5 運算子

下表是 Dart中定義的運算子,描述的運算子優先順序近似於Dart 解析器實際行為。

描述運算子
字首運算子 expr++    expr--    ()    []    .    ?.
字尾運算子 -expr    !expr    ~expr    ++expr    --expr   
倍數運算子 *    /    %  ~/
加減運算子 +    -
移位運算子 <<    >>    >>>
位與 &
位異或 ^
位或 |
關係運算符和測試運算子 >=    >    <=    <    as    is    is!
相等判斷 ==    !=   
邏輯與 &&
邏輯或 ||
判空運算子 ??
條件運算子 expr1 ? expr2 : expr3
級聯運算子 ..
賦值運算子 =    *=    /=   +=   -=   &=   ^=   etc.

建立表示式的時候會用到運算子。 下面是一些運算子表示式的例項:

a++
a + b
a = b
a == b
c ? a : b
a is T

在 運算子表 中, 每一行的運算子優先順序,由上到下依次排列,第一行優先順序最高,最後一行優先順序最低。 例如 %運算子優先順序高於 == , 而 == 高於 &&。 根據優先順序規則,那麼意味著以下兩行程式碼執行的方式相同:

// 括號可以提高可讀性。
if ((n % i == 0) && (d % i == 0)) ...

// 可讀性差,但是是等效的。
if (n % i == 0 && d % i == 0) ... 

警告: 對於有兩個運算元的運算子,運算子的功能由左邊的運算元決定。 例如, 如果有兩個運算元 Vector 和 Point, aVector + aPoint 使用的是 Vector 中定義的 + 運算子。

下面就對dart中的運算子進行常規意義的分類簡單列舉一下:

  • 算術運算子:+、-、*、/、~/(整除,結果為整數)、%(求餘運算)、++(自增)、--(自減)。(++、--分別有字首和字尾兩種表達形式,大家在學習的過程中要注意他們之間的區別)
    assert(2 + 3 == 5);
    assert(2 - 3 == -1);
    assert(2 * 3 == 6);
    assert(5 / 2 == 2.5); // 結果是雙浮點型
    assert(5 ~/ 2 == 2); // 結果是整型
    assert(5 % 2 == 1); // 餘數
    
    assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');
  • 關係運算符:==、!=、>、<、>=、<=,都是常規符號,不做其他解釋,大家要注意的是關係運算符的表示式結果為boolean型別。
    assert(2 == 2);
    assert(2 != 3);
    assert(3 > 2);
    assert(2 < 3);
    assert(3 >= 3);
    assert(2 <= 3);
  • 邏輯運算子:&&(邏輯與,短路運算)、||(邏輯或,短路運算)、!。
    //短路運算,即當左邊的表示式結果能確定最終結果時,右邊的表示式不再進行運算
      var a = 10;
      var b = a > 9 || a++ > 10; //a>9成立,又是或運算,所以b的結果為true,右邊的 a++ > 10不會進行計算,所以a的值不會加1
      print(a); //10
  • 賦值運算子:=、+=、-=、*=、/=。。。等一系列的擴充套件賦值運算子
    var a = 2; // 使用 = 複製
    a *= 3; // 複製並做乘法運算: a = a * 3
    assert(a == 6);
  • 位運算子:&(按位與運算)、|(按位或運算)、^(按位異或運算)、<<(按位左移)、>>(按位右移)。(所有的位運算都是以二進位制形式進行的)
    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
  • 條件運算子:
    •  condition ? expr1 : expr2 如果條件為 true, 執行 expr1 (並返回它的值), 否則, 執行並返回 expr2 的值。
    •  expr1 ?? expr2 如果 expr1 是 non-null, 返回 expr1 的值; 否則, 執行並返回 expr2 的值。
  • 級聯運算子(..):可以實現對同一個對像進行一系列的操作。 除了呼叫函式, 還可以訪問同一物件上的欄位屬性。 這通常可以節省建立臨時變數的步驟, 同時編寫出更流暢的程式碼。嚴格的來講, “兩個點” 的級聯語法不是一個運算子。 它只是一個 Dart 的特殊語法。
    // 第一句呼叫函式 querySelector() , 返回獲取到的物件。 獲取的物件依次執行級聯運算子後面的程式碼, 程式碼執行後的返回值會被忽略。
    querySelector('#confirm') // 獲取物件。
      ..text = 'Confirm' // 呼叫成員變數。
      ..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!'));
    
    // 級聯運算子可以巢狀,例如:
    final addressBook = (AddressBookBuilder()
          ..name = 'jenny'
          ..email = '[email protected]'
          ..phone = (PhoneNumberBuilder()
                ..number = '415-555-0100'
                ..label = 'home')
              .build())
        .build();
    
    // 在返回物件的函式中謹慎使用級聯操作符。 例如,下面的程式碼是錯誤的:
    var sb = StringBuffer();
    sb.write('foo')
      ..write('bar'); // Error: 'void' 沒喲定義 'write' 函式。
    // sb.write() 函式呼叫返回 void, 不能在 void 物件上建立級聯操作。
  • 型別判定運算子:as()、is(判定是否是指定型別或該型別子類的物件)、is! (跟is相反)。
    • 使用 as 運算子將物件強制轉換為特定型別。 通常,可以認為是 is 型別判定後,被判定物件呼叫函式的一種縮寫形式。 請考慮以下程式碼: 
if (emp is Person) {
  // 型別判斷
  // emp.firstName = 'Bob';

  // 下面這種寫法一般是沒問題的,進行型別強轉
  (emp as Person).firstName = 'Bob';
}

 

&n