dart語言基礎(chǔ)

此文章是v1.0+時(shí)編寫,年代久遠(yuǎn),小心有毒,謹(jǐn)慎食用!!!

一些重要概念

  • 所有的東西都是對象,所有的對象都是類的實(shí)例。即使 數(shù)字、函數(shù)、null
    也都是對象。所有的對象都繼承自 Object 類。
  • 指定靜態(tài)類型表明你的意圖,并使檢查類型檢查成為可能。在 Dart 1.x 指定類型是可選的,然而 Dart 正在朝著完全類型安全的語言發(fā)展。
  • 在 Dart 1.x 中,強(qiáng)類型 strong mode 是可選的,但在 Dart 2 中,默認(rèn)就是 strong mode
  • Dart 在運(yùn)行前解析所有的代碼,可以使用些小技巧,例如:通過使用類型或編譯時(shí)常量,來捕捉錯(cuò)誤或使代碼運(yùn)行的更快。
  • Dart 支持頂級的函數(shù),也支持類或?qū)ο蟮撵o態(tài)和實(shí)例方法。也可以在函數(shù)內(nèi)部嵌套函數(shù)或本地函數(shù)。
  • 同樣,Dart 支持頂級的變量,也支持類或?qū)ο蟮撵o態(tài)變量和實(shí)例變量(也被稱作字段或?qū)傩?。
  • Dart沒有 publicprotectedprivate 等關(guān)鍵字,如果一個(gè)標(biāo)識符以 _開頭則表示私有。
  • 標(biāo)識符以小寫字母或下劃線_開頭,后面跟著字符和數(shù)字的任意組合。
  • 明確區(qū)分表達(dá)式和語句。
  • Dart tools 會(huì)報(bào)告兩種類型的問題:警告(warnings)和錯(cuò)誤(errors)。警告僅標(biāo)志著你的代碼可能不會(huì)工作,但并不會(huì)阻止程序執(zhí)行;錯(cuò)誤可能是編譯時(shí)錯(cuò)誤,也可能是運(yùn)行時(shí)錯(cuò)誤。編譯時(shí)錯(cuò)誤會(huì)阻止程序執(zhí)行;運(yùn)行時(shí)錯(cuò)誤會(huì)在程序執(zhí)行時(shí)拋出異常。
  • Dart 1.x 有兩種運(yùn)行時(shí)模式:生產(chǎn)模式和檢查模式。推薦在開發(fā)和 debug 時(shí)使用檢查模式,部署到生產(chǎn)模式。生產(chǎn)模式時(shí) Dart 程序默認(rèn)的運(yùn)行時(shí)模式。
  • Dart 2 廢棄了 checked mode 默認(rèn) strong mode
  • Dart 2 不再使用 Dartium,改為 dartdevc(DDC)。
  • 沒有初始化的變量都會(huì)被賦予默認(rèn)值 null
  • final的值只能被設(shè)定一次。const 是一個(gè)編譯時(shí)的常量,可以通過 const 來創(chuàng)建常量值,var c=const[];,這里 c 還是一個(gè)變量,只是被賦值了一個(gè)常量值,它還是可以賦其它值。實(shí)例變量可以是 final,但不能是 const

關(guān)鍵字(Keywords)

abstract continue false new this
as default final null throw
assert deferred finally operator true
async do for part try
async* dynamic get rethrow typedef
await else if return var
break enum implements set void
case export import static while
catch external in super with
class extends is switch yield
const factory library sync yield

內(nèi)置類型(Built-in types)

  • numbers
  • strings
  • booleans
  • lists (also known as arrays)
  • maps
  • runes (for expressing Unicode characters in a string)
  • symbols

is is!操作符判斷對象是否為指定類型,如num、String等。

as用來類型斷言。

  • num
    • int 取值范圍:-2^53 to 2^53
    • double
// String -> int
var one = int.parse('1');

// String -> double
var onePointOne = double.parse('1.1');

// int -> String
String oneAsString = 1.toString();

// double -> String 注意括號中要有小數(shù)點(diǎn)位數(shù),否則報(bào)錯(cuò)
String piAsString = 3.14159.toStringAsFixed(2);
  • String

    • Dart 的String 是 UTF-16 編碼的一個(gè)隊(duì)列。
    • '...',"..."表示字符串。
    • '''...''',"""..."""表示多行字符串。
    • r'...',r"..."表示“raw”字符串。
  • bool

    • Dart 是強(qiáng) bool 類型檢查,只有 bool 類型的值是true 才被認(rèn)為是 true。
  • List

// 使用List的構(gòu)造函數(shù),也可以添加int參數(shù),表示List固定長度,不能進(jìn)行添加 刪除操作
var vegetables = new List();

// 或者簡單的用List來賦值
var fruits = ['apples', 'oranges'];

// 添加元素
fruits.add('kiwis');

// 添加多個(gè)元素
fruits.addAll(['grapes', 'bananas']);

// 獲取List的長度
assert(fruits.length == 5);

// 獲取第一個(gè)元素
fruits.first;

// 獲取元素最后一個(gè)元素
fruits.last;

// 利用索引獲取元素
assert(fruits[0] == 'apples');

// 查找某個(gè)元素的索引號
assert(fruits.indexOf('apples') == 0);

// 刪除指定位置的元素,返回刪除的元素
fruits.removeAt(index);

// 刪除指定元素,成功返回true,失敗返回false
fruits.remove('apples');

// 刪除最后一個(gè)元素,返回刪除的元素
fruits.removeLast();

// 刪除指定范圍元素,含頭不含尾,成功返回null
fruits.removeRange(start,end);

// 刪除指定條件的元素,成功返回null
fruits.removeWhere((item) => item.length >6);

// 刪除所有的元素
fruits.clear();
assert(fruits.length == 0);

// sort()對元素進(jìn)行排序,傳入一個(gè)函數(shù)作為參數(shù),return <0表示由小到大, >0表示由大到小
fruits.sort((a, b) => a.compareTo(b));
  • Map
// Map的聲明
var hawaiianBeaches = {
    'oahu' : ['waikiki', 'kailua', 'waimanalo'],
    'big island' : ['wailea bay', 'pololu beach'],
    'kauai' : ['hanalei', 'poipu']
};
var searchTerms = new Map();

// 指定鍵值對的參數(shù)類型
var nobleGases = new Map<int, String>();

// Map的賦值,中括號中是Key,這里可不是數(shù)組
nobleGase[54] = 'dart';

//Map中的鍵值對是唯一的
//同Set不同,第二次輸入的Key如果存在,Value會(huì)覆蓋之前的數(shù)據(jù)
nobleGases[54] = 'xenon';
assert(nobleGases[54] == 'xenon');

// 檢索Map是否含有某Key
assert(nobleGases.containsKey(54));

//刪除某個(gè)鍵值對
nobleGases.remove(54);
assert(!nobleGases.containsKey(54));
  • Runes
    Dart 中 runes 是UTF-32字符集的string 對象。
string = 'Dart';
string.codeUnitAt(0); // 68 返回指定位置index的16位UTF-16編碼單元
string.codeUnits;     // [68, 97, 114, 116] 返回一個(gè)只讀的List,包含字符串中所有的UTF-16編碼單元。 
  • Symbol

symbol字面量是編譯時(shí)常量,在標(biāo)識符前面加#。如果是動(dòng)態(tài)確定,則使用Symbol構(gòu)造函數(shù),通過new來實(shí)例化。

函數(shù)(Functions)

所有的函數(shù)都會(huì)有返回值。如果沒有指定函數(shù)返回值,則默認(rèn)的返回值是null
。沒有返回值的函數(shù),系統(tǒng)會(huì)在最后添加隱式的return 語句。

函數(shù)可以有兩種類型的參數(shù):

  • 必須的——必須的參數(shù)放在參數(shù)列表的前面。
  • 可選的——可選的參數(shù)跟在必須的參數(shù)后面。
可選的參數(shù)

可以通過名字位置指定可選參數(shù),但是兩個(gè)不能同時(shí)存在。

  • 命名可選參數(shù)使用{},默認(rèn)值使用冒號:,調(diào)用時(shí)需指明參數(shù)名,與順序無關(guān)。
  • 位置可選參數(shù)使用[],默認(rèn)值使用等號=,調(diào)用時(shí)參數(shù)按順序賦值。

操作符(Operators)

表中,操作符的優(yōu)先級依次降低。

描述 操作符
一元后置操作符 expr++ expr-- () [] . ?.
一元前置操作符 -expr !expr ~expr ++expr --expr
乘除 * / % ~/
加減 + -
位移 << >>
按位與 &
按位異或 ^
按位或
關(guān)系和類型判斷 >= > <= < as is is!
== !=
邏輯與 &&
邏輯或
若為null ??
條件表達(dá)式 expr1 ? expr2 : expr3
級聯(lián) ..
賦值 = *= /= ~/= %= += -= <<= >>= &= ^= = ??=

流程控制語句(Control flow statements)

  • if...else
  • for
  • while do-whild
  • break continue
  • switch...case
  • assert(僅在checked模式有效)

異常(Exceptions)

throw
  • 拋出固定類型的異常:
throw new FormatException('Expected at least 1 section');
  • 拋出任意類型的異常:
throw 'Out of llamas!';
  • 因?yàn)閽伋霎惓儆诒磉_(dá)式,可以將throw語句放在=>語句中,或者其它可以出現(xiàn)表達(dá)式的地方:
distanceTo(Point other) =>
    throw new UnimplementedError();
catch

將可能出現(xiàn)異常的代碼放置到try語句中,可以通過 on語句來指定需要捕獲的異常類型,使用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, s) {
  print('Exception details:\n $e');
  print('Stack trace:\n $s');
}

可以向catch()傳遞1個(gè)或2個(gè)參數(shù)。第一個(gè)參數(shù)表示:捕獲的異常的具體信息,第二個(gè)參數(shù)表示:異常的堆棧跟蹤(stack trace)。

rethrow

rethrow語句用來處理一個(gè)異常,同時(shí)希望這個(gè)異常能夠被其它調(diào)用的部分使用。

final foo = '';

void misbehave() {
  try {
    foo = "1";
  } catch (e) {
    print('2');
    rethrow;// 如果不重新拋出異常,main函數(shù)中的catch語句執(zhí)行不到
  }
}

void main() {
  try {
    misbehave();
  } catch (e) {
    print('3');
  }
}
finally

Dart 的finally用來執(zhí)行那些無論異常是否發(fā)生都執(zhí)行的操作。

final foo = '';

void misbehave() {
  try {
    foo = "1";
  } catch (e) {
    print('2');
  }
}

void main() {
  try {
    misbehave();
  } catch (e) {
    print('3');
  } finally {
    print('4'); // 即使沒有rethrow最終都會(huì)執(zhí)行到
  }
}

類(Classes)

Dart 是一種面向?qū)ο蟮恼Z言,并且支持基于mixin的繼承方式:一個(gè)類可以繼承自多個(gè)父類

使用new語句來構(gòu)造一個(gè)類。構(gòu)造函數(shù)的名字可能是ClassName,也可以是ClassName.identifier。例如:

var jsonData = JSON.decode('{"x":1, "y":2}');

// Create a Point using Point().
var p1 = new Point(2, 2);

// Create a Point using Point.fromJson().
var p2 = new Point.fromJson(jsonData);
  • 使用.來調(diào)用實(shí)例的變量或者方法。
  • 使用 ?. 來避免左邊操作數(shù)為null引發(fā)異常。
  • 使用const替代new來創(chuàng)建編譯時(shí)的常量構(gòu)造函數(shù)。
  • 兩個(gè)使用const構(gòu)建的同一個(gè)構(gòu)造函數(shù),實(shí)例相等。
  • 獲取對象的運(yùn)行時(shí)類型使用:o.runtimeType
實(shí)例化變量

在類定義中,所有沒有初始化的變量都會(huì)被初始化為null

所有實(shí)例變量會(huì)生成一個(gè)隱式的getter方法,不是finalconst的實(shí)例變量也會(huì)生成一個(gè)隱式的setter方法。

構(gòu)造函數(shù)
class Point {
  num x;
  num y;

  Point(num x, num y) {
    // 這不是最好的方式.
    this.x = x; // this關(guān)鍵字指向當(dāng)前類的實(shí)例
    this.y = y;
  }
}
class Point {
  num x;
  num y;

  // 推薦方式
  Point(this.x, this.y);
}
默認(rèn)構(gòu)造函數(shù)

沒有聲明構(gòu)造函數(shù),dart會(huì)提供默認(rèn)構(gòu)造函數(shù)。默認(rèn)構(gòu)造函數(shù)沒有參數(shù),并且調(diào)用父類的無參數(shù)構(gòu)造函數(shù)。

構(gòu)造函數(shù)不能被繼承

子類不會(huì)繼承父類的構(gòu)造函數(shù)。如果不顯式提供子類的構(gòu)造函數(shù),系統(tǒng)就提供默認(rèn)的構(gòu)造函數(shù)。

命名構(gòu)造函數(shù)
class Point {
  num x;
  num y;

  Point(this.x, this.y);

  // 命名構(gòu)造函數(shù)
  Point.fromJson(Map json) {
    x = json['x'];
    y = json['y'];
  }
}

使用命名構(gòu)造函數(shù)可以實(shí)現(xiàn)一個(gè)類多個(gè)構(gòu)造函數(shù)。構(gòu)造函數(shù)不能被繼承,父類中的命名構(gòu)造函數(shù)不能被子類繼承。如果想要子類也擁有一個(gè)父類一樣名字的構(gòu)造函數(shù),必須在子類實(shí)現(xiàn)這個(gè)構(gòu)造函數(shù)。

調(diào)用父類的非默認(rèn)構(gòu)造函數(shù)

默認(rèn)情況下,子類調(diào)用父類的無參數(shù)的非命名構(gòu)造函數(shù)。父類的構(gòu)造函數(shù)會(huì)在子類的構(gòu)造函數(shù)體之前(大括號前)調(diào)用。如果也使用了初始化列表 ,則會(huì)先執(zhí)行初始化列表 中的內(nèi)容,即下面的順序:

  1. 初始化列表
  2. 父類無參數(shù)構(gòu)造函數(shù)
  3. 主類無參數(shù)構(gòu)造函數(shù)

如果父類不顯式提供無參的非命名構(gòu)造函數(shù),在子類中必須手動(dòng)調(diào)用父類的一個(gè)構(gòu)造函數(shù)。在子類構(gòu)造函數(shù)名后,大括號{前,使用super.調(diào)用父類的構(gòu)造函數(shù),中間使用:分割。

父類構(gòu)造函數(shù)參數(shù)不能訪問this,例如,參數(shù)可以調(diào)用靜態(tài)方法但不能調(diào)用實(shí)例方法。

class Person {
  String firstName;

  Person.fromJson(Map data) {
    print('in Person');
  }
}

class Employee extends Person {
  // 父類沒有無參數(shù)的非命名構(gòu)造函數(shù),必須手動(dòng)調(diào)用一個(gè)構(gòu)造函數(shù) super.fromJson(data)
  Employee.fromJson(Map data) : super.fromJson(data) {
    print('in Employee');
  }
}

main() {
  var emp = new Employee.fromJson({});

  // Prints:
  // in Person
  // in Employee
  if (emp is Person) {
    // Type check
    emp.firstName = 'Bob';
  }
  (emp as Person).firstName = 'Bob';
}

因?yàn)楦割悩?gòu)造函數(shù)的參數(shù)是在被調(diào)用之前確認(rèn)值的,所以參數(shù)可以是一個(gè)表達(dá)式,像一個(gè)函數(shù)的調(diào)用。

class Employee extends Person {
  // ...
  Employee() : super.fromJson(findDefaultData()); // 一個(gè)函數(shù)調(diào)用作為參數(shù)
}

當(dāng)在構(gòu)造函數(shù)初始化列表中使用super()時(shí),要把它放在最后。

View(Style style, List children)
    : _children = children,
      super(style) {}
初始化列表

除了調(diào)用父類的構(gòu)造函數(shù),也可以通過初始化列表 在子類的構(gòu)造函數(shù)體前(大括號前)來初始化實(shí)例的變量值,使用逗號,分隔。如下所示:

一個(gè)初始化器的右邊不能訪問this

class Point {
  num x;
  num y;

  Point(this.x, this.y);

  // 在構(gòu)造函數(shù)體前 初始化列表 設(shè)置實(shí)例變量
  Point.fromJson(Map jsonMap)
      : x = jsonMap['x'],
        y = jsonMap['y'] {
    print('In Point.fromJson(): ($x, $y)');
  }
}
重定向構(gòu)造函數(shù)

有時(shí)候構(gòu)造函數(shù)的目的只是重定向到該類的另一個(gè)構(gòu)造函數(shù)。重定向構(gòu)造函數(shù)沒有函數(shù)體,使用冒號:分隔。

class Point {
  num x;
  num y;

  // 主構(gòu)造函數(shù)
  Point(this.x, this.y) {
    print("Point($x, $y)");
  }

  // 重定向構(gòu)造函數(shù),指向主構(gòu)造函數(shù),函數(shù)體為空
  Point.alongXAxis(num x) : this(x, 0);
}

void main() {
  var p1 = new Point(1, 2);
  var p2 = new Point.alongXAxis(4);
}
常量構(gòu)造函數(shù)

如果類的對象不會(huì)發(fā)生變化,可以構(gòu)造一個(gè)編譯時(shí)的常量構(gòu)造函數(shù)。定義格式如下:

  • 定義所有的實(shí)例變量是final
  • 使用const聲明構(gòu)造函數(shù)。
class ImmutablePoint {
  final num x;
  final num y;
  const ImmutablePoint(this.x, this.y);
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);
}
工廠構(gòu)造函數(shù)

當(dāng)實(shí)例化了一個(gè)構(gòu)造函數(shù)后,不想每次都創(chuàng)建該類的一個(gè)新的實(shí)例的時(shí)候使用factory關(guān)鍵字,定義工廠構(gòu)造函數(shù),從緩存中返回一個(gè)實(shí)例,或返回一個(gè)子類型的實(shí)例。

工廠構(gòu)造函數(shù)不能訪問this

class Logger {
  final String name;
  bool mute = false;
  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);// 命名構(gòu)造函數(shù)
  void log(String msg) {
    if (!mute) {
      print(msg);
    }
  }
}

main() {
  var p1 = new Logger("1");
  p1.log("2");

  var p2 = new Logger('22');
  p2.log('3');
  var p3 = new Logger('1');// 相同對象直接訪問緩存
}

方法
實(shí)例方法

實(shí)例方法可以訪問實(shí)例變量和this

Getters and setters

get()set()方法是Dart 語言提供的專門用來讀取和寫入對象的屬性的方法。每一個(gè)類的實(shí)例變量都有一個(gè)隱式的getter和可能的setter(如果字段為finalconst,只有getter)。

++之類的操作符,無論是否明確定義了getter,都按預(yù)期的方式工作。為了避免意想不到的副作用,操作符調(diào)用getter一次,將其值保存在臨時(shí)變量中。

class Rectangle {
  num left;
  num top;
  num width;
  num height;

  Rectangle(this.left, this.top, this.width, this.height);

  // Define two calculated properties: 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;
}

main() {
  var rect = new Rectangle(3, 4, 20, 15);
  assert(rect.left == 3);
  rect.right = 12;
  assert(rect.left == -8);
}
抽象方法

抽象方法,只有函數(shù)聲明,沒有函數(shù)體,以分號;結(jié)尾,作為接口在其他類中實(shí)現(xiàn)。調(diào)用抽象方法會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。如果在不是抽象類中定義抽象方法會(huì)引發(fā)warning,但不會(huì)阻止實(shí)例化。

abstract class Doer {
  ...

  void doSomething(); // 定義抽象方法
}

class EffectiveDoer extends Doer {
  void doSomething() {
    // ...Provide an implementation, so the method is not abstract here...
  }
}
可重寫的運(yùn)算符

下面是可重寫的運(yùn)算符,在運(yùn)算符前面使用operator關(guān)鍵字。

如果重寫==,需要重寫ObjecthashCodegetter。

< + { []
> / ^ []=
<= ~/ & ~
>= * << ==
% >>
抽象類

使用abstract關(guān)鍵字定義一個(gè)抽象類,抽象類不能實(shí)例化。抽象類通常用來定義接口。

假如需要將抽象類實(shí)例化,需要定義一個(gè)factory constructor

抽象類通常會(huì)包含一些抽象的方法。

abstract class AbstractContainer { // 抽象類
  // ....

  void updateChildren(); // 抽象方法
}
隱式接口

每一個(gè)類都隱式的定義一個(gè)接口,這個(gè)接口包含了這個(gè)類的所有實(shí)例成員和它實(shí)現(xiàn)的所有接口。

一個(gè)類可以實(shí)現(xiàn)一個(gè)或多個(gè)(用,隔開)接口,通過implements關(guān)鍵字。

class Person {
  final _name;
  Person(this._name);
  String greet(who) => 'hello,$who,i am $_name';
}

class Imposter implements Person {
  final _name = '';
  String greet(who) => 'hi $who.do you know who i am.';
}

greetBob(Person p) => p.greet('bob');
main(List<String> args) {
  print(greetBob(new Person('lili')));
  print(greetBob(new Imposter()));
}

繼承

使用extends來創(chuàng)造子類,使用super來指向父類。

class Television {
  void turnOn() {
    _illuminateDisplay();
    _activateIrSensor();
  }
  // ...
}

class SmartTelevision extends Television {
  void turnOn() {
    super.turnOn();
    _bootNetworkInterface();
    _initializeMemory();
    _upgradeApps();
  }
  // ...
}

子類可以重載實(shí)例方法,getter和setter。使用@override注解重載方法。

class A {
  @override
  void noSuchMethod(Invocation mirror) {
    print('You tried to use a non-existent member:' +
          '${mirror.memberName}');
  }
}

如果使用noSuchMethod()來實(shí)現(xiàn)每一個(gè)可能的getter,setter和一個(gè)或多個(gè)類型的方法,可以使用@proxy注解來避免警告:

@proxy
class A {
  void noSuchMethod(Invocation mirror) {
    // ...
  }
}

如果知道編譯時(shí)類型,可以不使用@proxy,只需要使類實(shí)現(xiàn)這些類型。

class A implements SomeClass, SomeOtherClass {
  void noSuchMethod(Invocation mirror) {
    // ...
  }
}
枚舉類型

枚舉類型是一種特殊的類,通常用來表示一組固定數(shù)字的常量值。

使用enum關(guān)鍵字聲明枚舉類型。

enum Color {
  red,
  green,
  blue
}

每個(gè)枚舉類型都有一個(gè)index的getter,返回以0開始的位置索引,每次加1。

assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);

使用values關(guān)鍵字獲取枚舉的所有值,返回一個(gè)列表。

print(Color.values); // [Color.red, Color.green, Color.blue]

在switch語句中使用枚舉,必須在case語句中判斷所有的枚舉,否則會(huì)獲得警告。

enum Color {
  red,
  green,
  blue
}
// ...
Color aColor = Color.blue;
switch (aColor) {
  case Color.red:
    print('Red as roses!');
    break;
  case Color.green:
    print('Green as grass!');
    break;
  default: //必須完成所有判斷,否則會(huì)引發(fā)warning
    print(aColor);  // 'Color.blue'
}

枚舉類型有以下限制:

  • 不能繼承,mixin,或?qū)崿F(xiàn)一個(gè)枚舉。
  • 不能顯式的實(shí)例化一個(gè)枚舉。
minxins

mixins是一種在多個(gè)類層次結(jié)構(gòu)中,重用一個(gè)類的代碼的方法。使用with關(guān)鍵字后跟多個(gè)mixin名。

class Musician extends Performer with Musical {
  // ...
}

class Maestro extends Person
    with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}
class變量和方法

使用static關(guān)鍵字來實(shí)現(xiàn)類范圍內(nèi)變量和方法。

  • 靜態(tài)變量

靜態(tài)變量在使用時(shí)初始化。

class Color {
  static const red =
      const Color('red'); // A constant static variable.
  final String name;      // An instance variable.
  const Color(this.name); // A constant constructor.
}

main() {
  assert(Color.red.name == 'red');
}
  • 靜態(tài)方法

靜態(tài)方法不能被實(shí)例調(diào)用,不能訪問this,只能被類名調(diào)用。

import 'dart:math';

class Point {
  num x;
  num 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);
  }
}

main() {
  var a = new Point(2, 2);
  var b = new Point(4, 4);
  var distance = Point.distanceBetween(a, b);
  print(distance); // 2.8284271247461903
}

對于通用或廣泛使用的工具和功能,應(yīng)該使用頂級函數(shù),而不是靜態(tài)方法。

可以使用靜態(tài)方法作為編譯時(shí)常量。例如,可以給一個(gè)常量構(gòu)造函數(shù),傳遞一個(gè)靜態(tài)方法作為參數(shù)。

泛型(Generics)

使用<...> 的方式來定義泛型。

為什么使用泛型
  • 雖然Dart 語言中類型是可選的,但是明確的指明使用的是泛型,會(huì)讓代碼更好理解。
 var names = new List<String>();
 names.addAll(['Seth', 'Kathy', 'Lars']);
 // ...
 names.add(42); // Fails in checked mode (succeeds in production mode).
  • 使用泛型減少代碼重復(fù)。

代碼片段一:

 abstract class ObjectCache {
   Object getByKey(String key);
   setByKey(String key, Object value);
 }

代碼片段二:

 abstract class StringCache {
   String getByKey(String key);
   setByKey(String key, String value);
 }

以上的兩段代碼可以使用泛型簡化如下:

 abstract class Cache<T> {
   T getByKey(String key);
   setByKey(String key, T value);
 }
用于集合類型(Using collection literals)

泛型用于ListMap 類型參數(shù)化。

  • List: <type>
  • Map: <keyType, valueType>
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'
};
在構(gòu)造函數(shù)中參數(shù)化類型
var names = new List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
var nameSet = new Set<String>.from(names);
var views = new Map<int, View>();
泛型集合及它們所包含的類型

dart的泛型類型是具體的,在運(yùn)行時(shí)包含它們的類型信息。

var names = new List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true

然而,is表達(dá)式檢查的是集合的類型,而不是里面的對象。在生產(chǎn)模式下,一個(gè)List<Stirng>可能有很多不是string的條目在里面。解決辦法是檢查每一項(xiàng),或者包裹在一個(gè)異常處理程序中。

限制參數(shù)化類型

當(dāng)實(shí)現(xiàn)一個(gè)泛型時(shí),如果需要限制它參數(shù)的類型,可以使用extends關(guān)鍵字。

// T 必須是SomeBaseClass或它的后代
class Foo<T extends SomeBaseClass> {...}

class Extender extends SomeBaseClass {...}

void main() {
  // It's OK to use SomeBaseClass or any of its subclasses inside <>.
  var someBaseClassFoo = new Foo<SomeBaseClass>();
  var extenderFoo = new Foo<Extender>();

  // It's also OK to use no <> at all.
  var foo = new Foo();

  // Specifying any non-SomeBaseClass type results in a warning and, in
  // checked mode, a runtime error.
  // var objectFoo = new Foo<Object>();
}

庫和可見性

使用importlibrary 指令可以方便的創(chuàng)建一個(gè)模塊或分享代碼。一個(gè)Dart 庫不僅能夠提供相應(yīng)的API,還可以包含一些以_開頭的私有變量僅在庫內(nèi)部可見。

每一個(gè)Dart 應(yīng)用都是一個(gè)庫,即使它沒有使用library指令。庫可以方便是使用各種類型的包。

使用庫(Libraries and visibility)

使用import指定怎樣的命名空間,從一個(gè)庫引用另一個(gè)庫。

import唯一要求的參數(shù)是指定庫的URI。

  • dart內(nèi)置庫,URI組合dart:
  • 其他庫,使用文件路徑或package:組合。package:組合式通過包管理工具提供的。
import 'dart:html';
import 'package:mylib/mylib.dart';
指定一個(gè)庫的前綴

如果導(dǎo)入的庫擁有相互沖突的名字,使用as為其中一個(gè)或幾個(gè)指定不一樣的前綴。

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// ...
Element element1 = new Element();           // Uses Element from lib1.
lib2.Element element2 = new lib2.Element(); // Uses Element from lib2.
導(dǎo)入庫的一部分

如果只需要使用庫的一部分內(nèi)容,使用showhide有選擇的導(dǎo)入。

// 僅導(dǎo)入foo.
import 'package:lib1/lib1.dart' show foo;

// 除了foo都導(dǎo)入
import 'package:lib2/lib2.dart' hide foo;
延遲加載庫

延遲加載,也叫懶加載,允許應(yīng)用程序按需加載庫。使用延遲加載的場景:

  • 減少程序初始化啟動(dòng)時(shí)間。
  • 執(zhí)行A/B測試——嘗試替換一個(gè)算法的實(shí)現(xiàn)。
  • 加載很少用的功能,比如可選的屏幕和對話框。

要延遲加載一個(gè)庫,首先必須使用deferred as導(dǎo)入它。

import 'package:deferred/hello.dart' deferred as hello;

當(dāng)需要使用庫的時(shí)候,使用庫名調(diào)用loadLibrary()

greet() async {
  // 使用await關(guān)鍵字暫停執(zhí)行,直到庫加載
  await hello.loadLibrary();
  hello.printGreeting();
}

可以在代碼中多次調(diào)用loadLibrary()方法。但是實(shí)際上它只會(huì)被執(zhí)行一次。

使用延遲加載的注意事項(xiàng):

  • 延遲加載的內(nèi)容只有在加載后才存在。
  • Dart 隱式的將deferred as改為了deferred as namespaceloadLibrary()返回值是Future

異步支持(Asynchrony support)

使用async函數(shù)和await表達(dá)式實(shí)現(xiàn)異步操作。

當(dāng)需要使用一個(gè)從Future返回的值時(shí),有兩個(gè)選擇:

  • 使用asyncawait
  • 使用Future API

當(dāng)需要從一個(gè)Stream獲取值時(shí),有兩個(gè)選擇:

  • 使用async和異步的循環(huán)(await for)。
  • 使用Stream API

代碼使用了asyncawait就是異步的,雖然看起來像同步代碼。

await lookUpVersion()

要使用await,代碼必須在函數(shù)后面標(biāo)記為async

checkVersion() async {
  var version = await lookUpVersion();
  if (version == expectedVersion) {
    // Do something.
  } else {
    // Do something else.
  }
}

可以使用try,catch和finally配合await處理錯(cuò)誤。

try {
  server = await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 4044);
} catch (e) {
  // 無法綁定到端口時(shí)......
}
聲明異步函數(shù)

異步函數(shù)是一個(gè)被async標(biāo)記的函數(shù)。

雖然異步的函數(shù)中可能執(zhí)行耗時(shí)的操作,但是函數(shù)本身在調(diào)用后將會(huì)立即返回,即使函數(shù)體一條語句也沒執(zhí)行。

checkVersion() async {
  // ...
}

lookUpVersion() async => /* ... */;

給函數(shù)添加async關(guān)鍵字將使函數(shù)返回一個(gè)Future類型。

// 修改前是同步的
String lookUpVersionSync() => '1.0.0';

// 修改后 是異步的 函數(shù)體不需要使用Future API
// dart會(huì)在必要的時(shí)候創(chuàng)建Future對象
Future<String> lookUpVersion() async => '1.0.0';
在 Future 中使用 await 表達(dá)式
await expression

可以在異步函數(shù)中多次使用await

var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);

await表達(dá)式中,表達(dá)式通常是一個(gè)Future。如果表達(dá)式不是Future 類型,它將自動(dòng)被包裝為Future類型。await expression的返回值是一個(gè)對象。await表達(dá)式使執(zhí)行暫停,直到對象可用。

如果await不工作,確保await處于async函數(shù)中。即使是在main函數(shù)中,也要標(biāo)記為async

main() async {
  checkVersion();
                          // 這里使用了 await
  print('In main: version is ${await lookUpVersion()}');
}
在Stream中使用異步循環(huán)
 // expression的值必須是Stram類型
await for (variable declaration in expression) {
  // Executes each time the stream emits a value.
}

異步循環(huán)的執(zhí)行流程如下:

  1. 等待 stream 發(fā)出數(shù)據(jù)。
  2. 執(zhí)行循環(huán)體,并將變量的值設(shè)置為發(fā)出的數(shù)據(jù)。
  3. 重復(fù)1.,2.直到stream 對象被關(guān)閉。

結(jié)束監(jiān)聽stram 可以使用breakreturen 語句,跳出for循環(huán),取消訂閱stream。

如果異步循環(huán)不工作,確保是在一個(gè)async函數(shù)中,如果使用異步循環(huán)在main()函數(shù)中,也要確保main()函數(shù)被標(biāo)記為async

main() async {
  ...
  await for (var request in requestServer) {
    handleRequest(request);
  }
  ...
}

可調(diào)用類(Callable classes)

Dart 語言中為了能夠讓類像函數(shù)一樣能夠被調(diào)用,可以實(shí)現(xiàn)call()方法。

class WannabeFunction {
  call(String a, String b, String c) => '$a $b $c!';
}

main() {
  var wf = new WannabeFunction();
  var out = wf("Hi","there,","gang");
  print('$out'); // Hi there, gang!
  print(wf.runtimeType); // WannabeFunction
  print(out.runtimeType); // String
  print(wf is Function); // true
}

隔離(Isolates)

Dart 代碼都運(yùn)行在獨(dú)立的隔離空間中,每個(gè)隔離空間都有自己的內(nèi)存堆棧,確保每個(gè)隔離空間的狀態(tài)不會(huì)被其它的隔離空間訪問。

類型定義(Typedefs)

typedef關(guān)鍵字,用來聲明一種類型,當(dāng)一個(gè)函數(shù)類型分配給一個(gè)變量時(shí),保留類型信息。

// 聲明一種 Compare類型
typedef int Compare(int a, int b);

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

main() {
  assert(sort is Compare); // True!
}

當(dāng)前 typedef 僅用于函數(shù)類型

元數(shù)據(jù)(Metadata)

元數(shù)據(jù)是以@開始的修飾符。在@ 后面接著編譯時(shí)的常量或調(diào)用一個(gè)常量構(gòu)造函數(shù)。

所有dart代碼中可用的三個(gè)注解:

  • @deprecated 被棄用的
  • @override 重載
  • @proxy 代理
定義自己的元數(shù)據(jù)

通過library來定義一個(gè)庫,在庫中定義一個(gè)相同名字的class,然后在類中定義const 構(gòu)造方法。

// 定義
library todo;

class todo {
  final String who;
  final String what;

  const todo(this.who, this.what);
}

// 使用
import 'todo.dart';

@todo('seth', 'make this do something')
void doSomething() {
  print('do something');
}

元數(shù)據(jù)可以修飾library(庫), class(類), typedef(類型定義), type parameter(類型參數(shù)), constructor(構(gòu)造函數(shù)), factory(工廠函數(shù)), function(函數(shù)), field(作用域), parameter(參數(shù)), 或者 variable declaration(變量聲明)。

可以使用reflection反射,在運(yùn)行時(shí)檢查元數(shù)據(jù)。

注釋(Comments)

  • 單行注釋:/
  • 多行注釋:/*...*/
  • 文檔注釋:/**...*//// 使用[]引用參考生成API文檔鏈接
class Llama {
  String name;// 這是單行注釋
  void feed(Food food) {
    /*
    這是多行注釋
    /
  }

/**
這里是文檔注釋
*/
  /// Exercises your llama with an [activity] for
  /// [timeLimit] minutes.
  void exercise(Activity activity, int timeLimit) {
    // ...
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,998評論 1 370
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,993評論 2 374

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