Dart 學習備忘錄

entronad發表於2018-07-17

Dart 官方文件學習筆記,記錄 Dart 特點和區別於其它語言之處

#基本特點

  • 任何變數都是物件,包括基本型別,所有物件都繼承自 Object 類
  • 強型別,自帶型別推斷,但也可指定型別
  • 有泛型
  • 支援最外層函式定義,也支援類的靜態方法與物件的例項方法,函式中可定義內部函式
  • 支援最外層變數定義,也支援類的靜態成員和物件欄位
  • 沒有表示私有公用的關鍵字,以“_”開頭的識別符號表示庫中私有
  • 識別符號以字母或“_”開頭,後面可隨意組合
  • 語句末尾要加分號

#變數與型別

  • 不指定型別定義變數的關鍵字是var,會根據初始化的值推斷型別,但一旦確定後不可賦給其它型別的值
  • 如需要定義可變型別的變數,可使用關鍵字dynamic或指定型別為Object
  • 任何型別變數沒有初始化,值都為null,表示“沒有”的只有null
  • 表示常量的關鍵字是final和const,他們可以代替var關鍵字或寫在型別之前
  • const是編譯時常量,在編譯時確定,不可定義為例項變數;final是執行時常量,在第一次使用時確定;一個常量是const型別也暗示必然是final型別
  • const也可定義為其它const的算式結果,在類中定義需與static一起
  • const可寫在變數或建構函式前用來定義常值,常量表示引用關係不可變,常值表示值本身不可變
  • 數值型別num,有兩個子類int,double,int可進行按位運算,沒有基本型別的概念,由於int,double都是num的子類,定義為num的變數可以在int、double間多型
  • 數值轉字串用int.parse('1'),double.parse('1.1'),字串轉數值用1.toString(),3.14159.toStringAsFixed(2)
  • 字串字面量中可用${}引用表示式,或直接$引用識別符號
  • 兩個字面相同的字串相等
  • 可用連續三個單引號或雙引號表示多行字串
  • 字串前加字母 r 則不處理轉義
  • 布林型別bool,所有需要判斷的地方只能傳入嚴格的bool結果
  • List型別,在定義時會推斷泛型
  • Map型別,key可為任意型別,在定義時會推斷泛型,用{}字面量定義的會推斷為Map型別
  • 在用建構函式新建物件時,可不用new關鍵字
  • 當用map[key]取值時,如沒有此key,會返回null
  • UTF-16字元用\uxxxx表示,其它長度編碼的UTF字元(不是4位)要加{}:\u{xxxxx}
  • 可通過#獲取識別符號的Symbol,壓縮會改變識別符號的名稱但不會改變它的Symbol

#函式

  • 函式的返回值、引數可設定型別也可不設,無返回值的函式返回型別可設為void
  • 只有一個表示式的函式可寫成箭頭函式
  • 函式的可選引數分為命名引數和位置引數
  • 命名引數的定義:
void enableFlags({bool bold, bool hidden}) {...}
複製程式碼
  • 命名引數的呼叫:
enableFlags(bold: true, hidden: false);
複製程式碼
  • 命名引數可用在引數很多很複雜的情況,定義時時,可通過@required確定其為必須的,需引入package:meta/meta.dart或package:flutter/material.dart包:
const Scrollbar({Key key, @required Widget child})
複製程式碼
  • 在函式定義時,形參表最後可用[]獲取位置引數:
String say(String from, String msg, [String device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}
複製程式碼
  • 可選引數可通過 = 設定預設值,不設定預設值為null
  • 應用必須有一個main函式:
void main() {
  
}

或

void main(List<String> arguments) {
  
}
複製程式碼
  • 函式定義式(包括前面帶上名字的箭頭函式)相當於定義了一個閉包型別的變數,等價於定義並將一個匿名函式賦值給一個變數,但不可以同時定義命名函式又賦值給變數

  • 作用域遵循詞法作用域,作用域是靜態確定的,函式可使用其定義時的變數

  • 任何函式都有返回值,如為顯示指明則返回null,故函式呼叫式一定是表示式

  • 多元運算子的版本由最左邊一個引數決定

  • ~/:整除(向下取整)

  • ==規則:如果涉及到null,必須兩邊都為null則返回true,否則返回false;如果不涉及null,返回左邊引數的x.==(y)方法結果

  • is;is!:判斷一個物件是(不是)一個類的實現(包括子類)

  • as:將一個物件強轉為一個類,如果該物件為null或不是該類將報錯

  • ??=:如果為null就賦值,否則不動

  • ^:按位異或

  • ~:按位取反

  • ??:條件運算子,如果左邊不為null返回左邊,否則返回右邊

  • ..:級聯運算子,表示把左邊物件中的右邊成員取出進行某項操作再返回該物件

  • ?.:條件獲取成員,如果左邊為null也不報錯而是返回null

#結構語句

  • else if中間分開一個空格

  • 可以用forEach()和for-in遍歷可迭代物件,比如Map,List

  • switch分支比對的目標必須是編譯時常量,被比較例項必須是同一類(不可以是子類),不可重寫==

  • 異常分為Exception和Error兩類以及它們的子類

  • 方法無需進行異常宣告和檢查

  • 可以throw任意物件,當然不建議這樣做

  • throw式是一個表示式

  • on指明捕捉的異常型別,catch指明異常引數,兩者可配合使用,有了on可省略catch

  • catch有第二個引數StackTrace

  • 在catch塊中,可用rethrow關鍵字繼續丟擲該異常

  • 就算遇到未捕獲的異常,也會先執行完finally中的異常再丟擲

#物件導向

  • 類只可以有一個父類,子類可使用任意級父類的內容
  • 對於定義了常量建構函式的類,可用const關鍵字呼叫,呼叫相同常量建構函式(包括引數)構造的物件是同一個例項
  • 在一個常量作用域內(比如一個常量Map)都是常量,不需要再重複寫const了
  • 物件類資訊的欄位是runtimeType
  • 類中宣告瞭初始值的欄位,欄位會在constructor之前初始化
  • 在類中只有命名衝突時才需要this.xx
  • 可以以如下的形式定義命名建構函式
Point.origin() {
    x = 0;
    y = 0;
  }
複製程式碼
  • 任何建構函式都不會被繼承
  • 如果沒有特別指明,子類的建構函式中第一步會呼叫父類的預設建構函式
  • 子類建構函式的執行順序是:初始化欄位表->父類建構函式->子類建構函式
  • 定義了建構函式會覆蓋掉預設建構函式,如父類沒有預設建構函式,子類建構函式需在函式體大括號前用冒號手動指明呼叫父類建構函式
class Employee extends Person {
  // Person does not have a default constructor;
  // you must call super.fromJson(data).
  Employee.fromJson(Map data) : super.fromJson(data) {
    print('in Employee');
  }
}
複製程式碼
  • 建構函式大括號前還可用冒號指明初始化欄位表,常用來設定final欄位:
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, num> json)
    : x = json['x'],
      y = json['y'] {
  print('In Point.fromJson(): ($x, $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);
}
複製程式碼
  • 固定不變的物件可設為編譯時常量,通過類定義中的常量建構函式實現:
class ImmutablePoint {
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);

  final num x, y;

  const ImmutablePoint(this.x, this.y);
}
複製程式碼
  • 如果不希望建構函式總是新建一個例項,新增了factory關鍵字成為工廠建構函式,工廠建構函式不可訪問this:
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);
  }
}
複製程式碼
  • 所有例項的欄位都有getter,非final的有setter,也可通過get、set關鍵字設定
  • 沒有定義函式體的方法稱為抽象方法,抽象方法只可出現在抽象類中
  • 通過abstract關鍵字可定義抽象方法,除非定義了工廠建構函式,抽象類不能被例項化
  • 任何類都是一個介面,一個類要實現一個介面(類),用關鍵字impliments,可以實現多個介面
  • 子類通過@override註解重寫父類方法
  • 可通過operator關鍵字重寫運算子(重寫 == 運算子需同時重寫 hashCode的getter)
  • 呼叫一個例項的未實現方法,將會報錯,觸發1.例項型別為dynamic;或2.例項定義了該方法(為抽象)同時重寫了noSuchMethod()
  • 列舉型別定義形式為:
enum Color { red, green, blue }
複製程式碼
  • 列舉型別中的每一個值都有一個index屬性,列舉類的values屬性可獲得所有的值
  • 不可以繼承、混入、實現列舉型別,也不可以例項化列舉型別
  • 建立mixin的要求是繼承Object,不宣告建構函式,不呼叫super
  • 使用mixin在類定義後使用關鍵詞with,和混入多個mixin
  • 可用static關鍵字定義類變數和類方法,類變數在第一次使用時才會初始化;類方法不可使用this;對於公有功能,建議使用函式而不是類方法
  • 除了限制型別,泛型還能起到指代型別,減少程式碼的作用:
abstract class Cache<T> {
  T getByKey(String key);
  void setByKey(String key, T value);
}
複製程式碼
  • 可在集合的字面量定義中使用泛型
var names = <String>['Seth', 'Kathy', 'Lars'];
var pages = <String, String>{
  'index.html': 'Homepage',
  'robots.txt': 'Hints for web robots',
  'humans.txt': 'We are people, not machines'
};
複製程式碼
  • 集合型別機制是reified(不同於Java的erasure),會在執行時保留
print(names is List<String>);
複製程式碼

#非同步處理

  • 處理非同步過程的類是Future和Stream,函式是async/await函式
  • async關鍵字寫在參數列與函式體之間
  • 可將main函式定義為async函式
  • 不需要返回值的async函式返回型別可設為Future<void>
  • 可用await for等待遍歷stream的所有值,但要小心使用:
await for (varOrType identifier in expression) {
  // Executes each time the stream emits a value.
}
複製程式碼
  • 同步generator用sync*關鍵字定義,返回Iterable,非同步generator用async*關鍵字定義,返回Stream
  • 遞迴generator函式中呼叫自身時可用yield*關鍵字提高效能

#其它

  • 當給類實現了call()方法,類的例項就可以像函式一樣被呼叫了

  • 通過isolate處理多執行緒,isolate有單獨的記憶體堆,相互之間不可訪問

  • 通過typedef關鍵字,可定義函式的具體(引數、返回值)型別:

typedef Compare<T> = int Function(T a, T b);

int sort(int a, int b) => a - b;

void main() {
  assert(sort is Compare<int>); // True!
}
複製程式碼
  • 文件級註釋用///或/**開頭,編譯器會忽略文件級註釋,但其中用[]括起來的內容仍然可以連結

相關文章