【翻譯】dart語言預覽

官方文檔

一個基本的程序

// Define a function.
printInteger(int aNumber) {
  print('The number is $aNumber.'); // Print to console.
}

// This is where the app starts executing.
main() {
  var number = 42; // Declare and initialize a variable.
  printInteger(number); // Call a function.
}
複製代碼
  • //Define a function.javascript

    單行註釋塊.Dart還支持多行註釋塊/* */html

  • intjava

    一個類型,其餘常見的list,String,boolgit

  • 42github

    常量做爲一個數字web

  • print()正則表達式

    一個便利的方法顯示輸出算法

  • '....'("....")express

    字符串文字編程

  • $variableName(or $expresss)

    接受常量或者表達式,在字符串中包含變量或者表達式

  • main()

    main()是必須的,特殊的,頂級的函數,做爲程序執行的入口

  • var

    一個方法聲明變量沒有指定他的類型

備註:這個網站代碼遵循着 dart風格指南(Dart style guide)

重要的概念

當你學習dart語言,記住這些事實和概念

  • 全部能夠放入變量中的都是一個對象,每個對象都是類的實例。每個數字,函數,甚至null都是一個對象。全部的對象都繼承於對象類。

  • 雖然Dart語言是一個強類型的,可是準肯定義類是可選的。由於Dart能夠推斷類型。在上面的代碼中,number被推出是int類型,當你預料不到這個類型時,使用這個特殊的類dynamic

  • Dart支持範類型,像List<int>(整數列表)或者List<dynamic>(任何類型的列表)

  • Dart支持頂級的函數(如mian()),也支持函數關聯在類或者對象上(分別時靜態方法或者實例方法),你也能夠建立一個函數在一個函數內(嵌套函數或局部函數)

  • 不像javaDart沒有關鍵字 public, protected,和 private,若是一個標識符開始是下劃線(_),對這個庫而言它是私有的。

  • 標識符開始於一個字母或者下劃線(_),接下來能夠是任何字母或者數字的組合。

  • Dart語言同時具備表達式(具備運行時值)和語句(不具備運行時值),例如條件表達式condition ? expr1 : expr2 其值不是expr1就是expr2,而if-else 則沒有。一個語句同時包含一個或者多個表達式,但一個表達式不能直接包含語句。

    runtime values直譯來是運行時值。

    if (expression) { statement(s) } else { statement(s) }
    //這個語句沒有runtime values,也就是不能複製給變量。
    //而改寫
    var a=condition ? expr1 : expr2;
    // ----------------------
    var a;
    if(condition){
        a=expr1;//只能在這裏寫複製表達式
    } else {
        a=expr2;//只能在這裏寫複製表達式
    }
    複製代碼
  • Dart工具能夠報告出兩種問題:warning(警告)和errors(錯誤)。警告僅僅代表你的代碼可能有問題,但他們不阻止代碼的執行,錯誤是編譯錯誤,也能夠是運行時出錯,編譯錯誤將沒法運行,運行時錯誤,將在程序執行時發生異常

關鍵字

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

避免使用這些關鍵字做爲標識符。可是,若是須要,用上標標記的關鍵字是能夠作標識符的。

  • 上標1是上下文關鍵詞,只有在特殊的地方他纔有意義,他們在任何地方是有效的標識符。

  • 上標2內嵌標識符,爲了方便從JavaScript代碼移植到Dart代碼,這些關鍵詞是有效的在大多數地方,可是他們不能用於類名或者類型名,也不能作import 前綴

  • 上標3是在Dart1.0後加的,用於支持異步的特性,你不能使用await或者yield做爲一個標識符在任何一個函數體使用asyncasync或者sync**內。

表中的全部其餘單詞都是保留字,不能是標識符。

變量

建立並初始化一個變量

var name = 'Bob';
複製代碼

變量是一個引用。這個被命名爲name的變量包含了一個對String對象的引用,該對象的值爲「Bob」。

能夠看出標識符爲name的變量是一個String(字符串),可是你也能夠明確指定它的類型。若是一個對象不侷限於單一類型,請按設計指南(design guidelines指定Object或者dynamic類型

dyname name = 'Bob';
複製代碼

明確的聲明這個變量

Sting name = 'Bob';
複製代碼

這個頁面遵循 Dart語言風格指南推薦(style guide recommendation)。對局部變量使用var,而不是明確指定它的類型

默認值

未初始化的變量有一個默認的值null。甚至一個數字類型的變量也被初始化爲null,由於Dart中數字和其餘任何類型同樣都是對象。

int lineCount;
assert(lineCount==null);  
複製代碼

備註:assert(condition)的只會在程序開發期間生效,若是condition是false將會拋出一個錯誤。正式的代碼將會忽略assert()的調用。

finalconst

若是你從不想改變一個變量,使用final或者constfinal變量只能被設置一次,const變量編譯運行時不變。(const變量也是final。)頂層的final變量或者類中的final在第一次使用的時候才進行初始化。

實例變量只能使用final,而不能使用const。final 實例變量必須初始化在final變量聲明處(構造體)以前,可使用構造函數傳參或者使用構造函數的 初始化列表initializer list來初始化。

建立和設置final變量的例子

final name = 'Bob';   
final String nickname = 'Bobby';
複製代碼

你不能該改變final的變量的值

name = 'Alice';     
// Error: a final variable can only be set once.
複製代碼

使用const能夠定義編譯時常量(這裏是compile-time constants:編譯時常量)。若是const變量是claas級別的,標記爲static const。在變量聲明處,將能夠將數字或者字符字面量、const變量或對常數進行算數的結果設置爲編譯時常量

const bar = 1000000; // Unit of pressure (dynes/cm2)
const double atm = 1.01325 * bar; // Standard atmosphere
複製代碼

const關鍵字不只僅用於聲明常量(字面量、數字或者cosnt變量)變量。還可使用它來建立常量值,以及聲明建立常量值的構造器。任何變量均可以有一個常量。

var foo = const [];
final bar = const [];
const baz = []; //等效於 `const []`
複製代碼

像上面的baz同樣,初始化表達式上省略const。詳細不要使用冗餘的使用const(DON’T use const redundantly)

你能夠改變非final、非const的變量,即便它有一個const的值:

//foo聲明處在上一個例子中
foo = [1, 2, 3]; // Was const [] 
複製代碼

你沒法更改const聲明的變量

baz = [42]; // Error: Constant variables can't be assigned a value.
複製代碼

dart2.5開始,你能夠定義const使用 類型檢測和轉換(type checks and casts)isand)、集合if和集合for(使用if和for構建集合的語句)、拓展運算符(......?):

// 如下在dart2.5中有效
const Object i = 3; //i是具備int值的const對象
const list = [i as int]; // 使用類型轉換。
const map = {if (i is int) i: "int"}; // 使用is和collection if.
const set = {if (list is List<int>) ...list}; // ...拓展運算符.
複製代碼

有關const的更多信息,參閱List,Maps和Classes。

內置類型

Dart 語言對如下類型提供了特殊的支持:

  • number
  • string
  • boolean
  • lists(也被稱之爲arrays)
  • maps
  • runes(用於在字符表示Unicode編碼)
  • symbols.

你可使用字面量(字面量就是輸給編程語言的文字)來初始化這些特殊的類型。例如,'this is a string'是一個string的字面量,true是一個boolean的字面量

由於在dart中每個變量都是一個對象即一個實例化的類。因此你可使用構造函數去初始化。一些內置的類型有本身的構造函數。例如你可使用Map()構造函數去建立map

Nubers

Dart中只有兩種:

int

整形的值不大於64位,具體取決於平臺。在Dart VM,值的範圍爲-263到263,編譯成JavaScript的Dart,值的範圍爲 -253到253

double

64位(雙倍精度)浮點數,遵行IEEE 754的標準的規定。

int和double都是num的子類。num類型包括基礎的運算符,例如+,-,/和*,你也能夠找到abs(),ceil(),和floor()以及其餘的方法(按位運算符,如>>,是定義在int類的)若是num和他的子類沒有你要的內容,dart:math可能能幫到你。

整形數字沒有小數。

var x = 1;
var hex = 0xDEADBEEF;//16進制
複製代碼

若是一個數包括小數它是double。

var y = 1.1;
var exponents = 1.142e5;
複製代碼

從Dart2.1開始,必要時,整形變量自動轉化爲doubles。

double z = 1; // 等效於double z = 1.0.
複製代碼

版本:在Dart2.1以前,它是錯誤的。使用整形的變量儲存double類型的自賣能量

類型的轉化,string轉化爲number,number轉化爲string

// 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 類型支持位移運算符(<<,>>) AND (&), 和 OR (|)運算符 。例如:

assert((3 << 1) == 6); // 0011 << 1 == 0110
assert((3 >> 1) == 1); // 0011 >> 1 == 0001
assert((3 | 4) == 7); // 0011 | 0100 == 0111
複製代碼

number字面量是編譯時的常量,許多數學表達式只要操做的數是編譯時常量,則結果也是編譯時常量。

const msPerSecond = 1000;
const secondsUntilRetry = 5;
const msUntilRetry = secondsUntilRetry * msPerSecond;
複製代碼

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.";
複製代碼

你能夠經過${express}把表達式的值放入字符串中,若是表達式只是是標識符,你須要省略{}。爲了獲取對象相對應的字符串,dart調用該對象的方法toString()方法(如:print([1,2])先調用[1,2].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

var s = r'In a raw string, not even \n gets special treatment.';
複製代碼

參考 Runes 來了解如何在字符串 中表達 Unicode 字符。

字符串中只要插值表達式是常量(null、數值、字符串、布爾值),則其結果也是一個常量。

// These work in a const string.
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';

// These do NOT work in a const string.
var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = [1, 2, 3];

const validConstString = '$aConstNum $aConstBool $aConstString';
// const invalidConstString = '$aNum $aBool $aString $aConstList';
複製代碼

使用字符串的更多信息請參考: 字符串和正則表達式

Booleans

爲了表達布爾值,dart有一個類型bool,只有兩個對象是bool類型,truefalse,二者都是常量。

dart的類型安全意味着,你不能使用if(nonbolleanValue)或者assert(nonbooleanValue)(不是布爾類型的值)。相反,應該像這樣顯示檢查。

// 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);
複製代碼

lists

在幾乎絕大數語言中都有數組(有序數組)這個集合類型。在dart中,數組是List。因此咱們一般都稱之爲lists。

dart語言的數組很像JavaScript數組。這裏有一些簡單的例子。

var list  = [1,2,3];
複製代碼

dart 推斷上面的list是list<int>類型。若是你嘗試添加一個非整數類型的對象到這個list中,程序分析器或者運行環境會拋出一個錯誤。想要更多信息,閱讀type inference.

列表使用從0開始的索引。0爲list的第一個元素,list.length-1是list最後一個元素。你能夠像JavaScript同樣獲得字符串的長度或者引用列表。

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

list[1] = 1;
assert(list[1] == 1);
複製代碼

在列表以前添加const,能夠建立常量列表。

var constantList = const [1, 2, 3];
// constantList[1] = 1; // Uncommenting this causes an error.
複製代碼

dart2.3引進拓展運算符(spread operator)(...)和空感知擴展運算符(...?) ,提供一個簡明的方法在容器中插入大量的元素。

例如,你可使用拓展運算符(...)插入列表中所有的元素到另一個列表中。

var list = [1, 2, 3];
var list2 = [0, ...list];
assert(list2.length == 4);
複製代碼

若是拓展運算符右邊的表達式爲空,你使用空感知拓展運算符(...?)避免異常:

var list;
var list2 = [0, ...?list];
assert(list2.length == 1);
複製代碼

點擊spread operator獲取拓展運算符更多的用法和例子

Dart2.3也引入了 collection if 和**collection for ** ,你可使用條件語句(if)和循環語句(for)在容器中添加元素。

collection if 去建立三個或四個元素

var nav = [
  'Home',
  'Furniture',
  'Plants',
  if (promoActive) 'Outlet'
];//promoActive是true 爲四個元素,反之爲三個元素。
複製代碼

collection for去添加大量的元素到列表中。

var listOfInts = [1, 2, 3];
var listOfStrings = [
  '#0',
  for (var i in listOfInts) '#$i'
];
assert(listOfStrings[1] == '#1');
複製代碼

點擊 control flow collections proposal.獲取更多collection if和 collection for的例子。

List類有不少操做列表方便的方法,點擊 範類(Generics)集合(Collections)獲取更多的信息。

Sets

在dart中set是無序無重複元素的集,set字面量和 Set類型提供sets的支持。

雖然Set是核心庫裏的內容,但set是dart2.2才被引入的。

經過set字面量建立sets

var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
複製代碼

dart判斷上面的halogens變量是Set。若是你嘗試添加一個錯誤的類型至set中,程序分析器或者運行環境會拋出一個錯誤。想要更多信息,閱讀type inference.

使用類型後加{}或者將{}分配給Set類型的變量,建立空的set

var names = <String>{};
// Set<String> names = {}; // This works, too.
// var names = {}; // Creates a map, not a set.
複製代碼

是Set仍是map?map的語法和set語法是類似的。由於map,{}默認爲map類型。若是你忘記變量或者{}類型的註解,那麼dart將建立一個Map<dynamic, dynamic>.的對象。

添加元素使用add()或者addAll()方法

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.
複製代碼

Dart2.3起,sets拓展運算符(......?)和collection ifs 和 fors,就像lists同樣。點擊 spread operator proposalcontrol flow collections proposal,獲取更多拓展運算符和collection 用法和信息。

點擊 Generics and Sets或者更多sets的信息。

Maps

一般,map是關聯鍵和值的對象。鍵和值均可以是任何類型的對象。每一個鍵只能出現一次,而一個值能夠重複屢次。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判斷giftsMap<String, String> nobleGasesMap<int, String>。若是你嘗試添加一個錯誤的類型至二者的map中,程序分析器或者運行環境會拋出一個錯誤。想要更多信息,閱讀type inference.

你能夠建立同樣的對象經過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';
複製代碼

你可能但願看到new Map()而不是Map()。dart 2起,new關鍵字是可選的。點擊 Using constructors.獲取更多的信息。

添加一個鍵值對到一個現存的map中和JavaScript方法同樣。

var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds'; // Add a key-value pair
複製代碼

獲取一個值的方法也和JavaScript一致

var gifts = {'first': 'partridge'};
assert(gifts['first'] == 'partridge');
複製代碼

若是你查找的鍵不在map中,你會獲得null

var gifts = {'first': 'partridge'};
assert(gifts['fifth'] == null);
複製代碼

使用 .length獲取map鍵值的個數

var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds';
assert(gifts.length == 2);
複製代碼

在map字面量前使用const建立map編譯時的常量。

final constantMap = const {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};

// constantMap[2] = 'Helium'; // Uncommenting this causes an error.
複製代碼

Dart2.3起,maps支持拓展運算符(......?)和collection ifs 和 fors,就像lists同樣。點擊 spread operator proposalcontrol flow collections proposal.,獲取更多拓展運算符和collection 用法和信息。

點擊 Generics and Maps或者更多maps的信息。

Runes

在Dart中,runes表明字符串的utf-32 code points。

Unicode 規定一個惟一的數字標量對世界上全部書寫系統的每個單詞,數字和符號。由於dart字符串是由UTF-16編碼單元組成的序列,表達32位的Unicode值須要用到特殊的語法。

一般的方法表達Unicode code points 是 \uXXXX,XXXX是4位十六進制的值。 例如,心形符號 (♥) 是 \u2665。 對於非 4 個數值的狀況, 把編碼值放到大括號中便可。 例如,笑臉 emoji (😆) 是 \u{1f600}

String 類型有有一系列屬性你能夠提取rune的信息。codeUnitAtcodeUnit屬性返回一個16位的代碼單元。使用 runes 屬性來獲取字符串的 runes 信息。

下面是一些例子

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));
}
//👏
//[55357, 56399]
//[128079]
//♥ 😅 😎 👻 🖖 👍
複製代碼

使用 list 操做 runes 的時候請當心。你所操做的特定的語種、字符集和操做方式,可能致使你的字符串出問題。 更多信息參考 Stack Overflow 上的一個問題: 如何在 Dart 中反轉一個字符串?

Symbols

Symbol 對象表明在Dart程序中聲明的運算符或者操做符。你可能歷來不會用到symbols,可是symbols對於名稱引用的標識符的api時極有價值的,由於縮小標識符的名字不會改變標識符的symbols。

使用 # 後跟標識符,獲得一個symbol字面量

#radix
#bar
複製代碼

symbol字面量是編譯時的常量

函數

dart是面向對象的語言, Function.是一個對象,而且具備一個類型。這個意味着函數能夠分配給變量,也能夠做爲參數傳給其餘函數。你也能像函數同樣調用類的實例。點擊 Callable classes.得到更多信息。

這裏是實現函數的例子

bool isNoble(int atomicNumber) {//atomicNumber原子序數
  return _nobleGases[atomicNumber] != null;   //_nobleGases稀有氣體
}
複製代碼

雖然Effective Dart推薦公共APIS有類型註釋,可是忽略類型函數仍然是有效的。

isNoble(atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}
複製代碼

對於只有一個表達式的式子,可使用簡略語法

bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
複製代碼

=> expr語法是{ return expr; }.簡略的表達式。 =>符號也被稱做箭頭函數。

僅僅一個表達式(非語句)能夠放在箭頭(=>)和分號 (;)之間。你不能將一個條件語句 (if statement)放到這裏,可是能夠放條件表達式(conditional expression.)

函數能夠有兩個參數:必選的和可選的。首先列出的是必選參數,接着是可選的參數。可選參數能夠是命名可選參數或者位置可選參數。

注意:一些api尤爲是 Flutter 小部件構造函數只使用命名參數,即便對於強制參數也是如此。有關詳細信息,請參見下一節。
複製代碼

可選參數Optional parameters

可選參數能夠是位置參數,也能夠是命名參數。但不能是二者都是。

可選命名參數Optional named parameters

可選命名參數。調用函數時,你可使用paramName: value指定參數。

enableFlags(bold: true, hidden: false);
複製代碼

當定義一個函數時,使用{param1, param2, …}指定命名參數。

/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold, bool hidden}) {...}
複製代碼

雖然命名參數時是可選的,你可使用 @required來註釋一個命名可選的參數是強制的,用戶必須爲此參數提供值。

const Scrollbar({Key key, @required Widget child})
複製代碼

當構建Scrollbar時,分析器會在缺乏child參數時提示錯誤。但運行時不會拋出錯誤。(這個是Flutter中的內容)

要使用@required依賴於 meta庫,請導入package:meta/meta.dart

位置可選參數Positional parameters

在函數參數內使用[]包裹的參數,是位置可選參數。

String say(String from, String msg, [String device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}
複製代碼

不寫位置參數,調用這個函數

assert(say('Bob', 'Howdy') == 'Bob says Howdy');
複製代碼

寫位置參數,調用這個函數

assert(say('Bob', 'Howdy', 'smoke signal') ==
    'Bob says Howdy with a smoke signal');
複製代碼

設置參數的默認值Default parameter values

你的函數可使用=命名可選參數位置可選參數添加默認值。默認值必須是常量。若是沒有定義默認值,這個默認值爲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);
複製代碼

老代碼可能會使用冒號(:)而不是(=)設置默認值。緣由是,原代碼最初只有:是支持的。:設置命名參數默認值可能在之後版本不能使用,因此咱們推薦你使用=設置默認值。

設置默認值爲位置參數

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');
複製代碼

你也可使用lists 或者maps做爲默認值,下面定義了一個doStuff(),併爲listgifts設置了默認值

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');
}
複製代碼

主函數The main() function

每個程序必須有一個頂層函數main(),做爲程序的入口。main()返回void和有一個可選的參數 List<String>

void main() {
  querySelector('#sample_text_id')
    ..text = 'Click me!'
    ..onClick.listen(reverseText);
}
複製代碼

在上面的代碼中..語法叫作[cascade](#Cascade notation (..)).使用級聯,您能夠對單個對象的成員執行多個操做。

main()一個命令行執行程序,它接收參數

// 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');
}
複製代碼

你可使用args library去定義和解析命令行參數。

函數做爲一等對象Functions as first-class objects

你能夠將函數做爲參數做爲一個參數傳給另外一個參數。

void printElement(int element) {
  print(element);
}

var list = [1, 2, 3];

// 將printElement做爲參數傳遞。
list.forEach(printElement);
複製代碼

你也能夠將函數分配給一個變量

var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
複製代碼

這個例子使用了一個匿名函數。下一節將介紹更多信息。

匿名函數Anonymous functions

絕大數函數是命名函數,如main()或者printElement(),你也能夠建立一個不須要名字的函數,稱之爲匿名函數。有時是lambda或者閉包函數。你分配匿名函數給一個變量。例如,你能夠從一個集合中添加或者刪它。

匿名函數看起來像明明函數——零個或者多個參數,括號內用逗號分隔和可選類型註釋分隔。

下面註釋塊包含函數的主體

([[Type] param1[, …]]) { codeBlock; };

用無類型的參數item. 定義匿名函數。這個函數,爲列表中的每一個項調用,打印一個字符串,該字符串包含指定索引處的值。

var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});
//0: apples
//1: bananas
//2: oranges
複製代碼

若是函數僅包含一個語句,你可使用箭頭函數簡化。

list.forEach(
    (item) => print('${list.indexOf(item)}: $item'));
複製代碼

靜態做用域Lexical scope

Dart 是靜態做用域語言,變量的做用域是由變量的位置肯定的。您能夠跟隨大括號向外查看變量是否在做用域中。

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);
    }
  }
}
複製代碼

注意 nestedFunction() 如何使用每一個級別的變量。

閉包 Lexical closures

閉包是一個函數對象,無論在何處調用,該對象均可以訪問其做用域內的變量。

函數能夠在周圍定義處封閉變量,在如下示例中,makeAdder()得到變量addBy。不管返回的函數在哪裏,它都會保留addBy的值。

/// 返回一個將[addBy]添加到的函數
/// function's argument.
Function makeAdder(num addBy) {
  return (num i) => addBy + i;
}

void main() {
  // 建立一個加2的函數
  var add2 = makeAdder(2);

  // 建立一個加4的函數。
  var add4 = makeAdder(4);

  assert(add2(3) == 5);
  assert(add4(3) == 7);
}
複製代碼

相等性測試函數Testing functions for equality

下面是測試頂級函數、靜態方法和實例方法是否相等的示例

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);
}
複製代碼

返回值Return values

全部的函數都返回一個值,若是沒有返回一個指定的值,這個表達式將隱式地附加到函數體return null

foo() {}

assert(foo() == null);
複製代碼

Operators

在接下的表格中,你能夠看到Dart定義的運算符。如[運算符重寫操做Overridable operators](overridable operators).所示你能夠重寫這些運算符。

Description Operator
後綴unary postfix expr++ expr-- () [] . ?.
前綴unary prefix -expr !expr ~expr ++expr --expr
乘除multiplicative * / % ~/
添加additive + -
位移shift << >> >>>
按位與bitwise AND &
按位異或bitwise XOR ^
按位或bitwise OR |
關係運算符relational and type test >= > <= < as is is!
相等equality == !=
邏輯與logical AND &&
邏輯或logical OR ||
判斷空if null ??
控制conditional expr1 ? expr2 : expr3
級聯cascade ..
賦值assignment = *= /= += -= &= ^=

警告:運算符優先級是Dart解析器行爲的近似值。要得到明確的答案,請參閱 Dart語言規範中的語法.

當你使用運算符,你建立一個表達式。這裏是一些例子運算符的表達式。

a++
a + b
a = b
a == b
c ? a : b
a is T
複製代碼

表格中,每一個操做符的優先級都高於其後行的操做符。例如,multiplicative %運算符的優先級高於equality ==(所以multiplicative 在以前進行)。equality ==的優先級高於logical AND operator &&,所以下面兩行代碼按照相同的方式運行

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

// 等效
if (n % i == 0 && d % i == 0) ...
複製代碼

警告:對於一個二元運算符,使用左邊的運算符操做方式。例如,你有一個Vector 的對象和一個Point 的對象相加aVector + aPoint ,使用的是Vector 版本的 +(重寫運算符時)

Arithmetic operators

Dart支持經常使用的算術運算符,以下表所示。

Operator Meaning
+ 求和
相減
-expr 一元減號,也稱爲否認(反轉表達式的符號)
* 相乘
/ 相除
~/ 相除,返回一個整數結果
% 獲取整數除法的餘數(模數)

例子:

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還支持前綴和後綴的自增自減運算符

Operator Meaning
++var var = var + 1 (表達式的值是 var + 1)
var++ var = var + 1 (表達式的值是var)
--var var = var – 1 (表達式的值是 var – 1)
var-- var = var – 1 (表達式的值是 var)

例子:

var a, b;

a = 0;
b = ++a; // a先自增再賦值給b
assert(a == b); // 1 == 1

a = 0;
b = a++; // a先賦值給b後自增
assert(a != b); // 1 != 0

a = 0;
b = --a; // a先自減再賦值給b
assert(a == b); // -1 == -1

a = 0;
b = a--; // a先賦值再自減
assert(a != b); // -1 != 0
複製代碼

Equality and relational operators

下表列出了相等和關係運算符的含義。

Operator Meaning
== 相等運算符
!= 不等運算符
> 大於運算符
< 小於運算符
>= 大於等於運算符
<= 小於等於運算符

爲了測試兩個對象x,y是一樣的東西,使用==運算符(在極少數的狀況下,你須要知道兩個對象是不是徹底相同的對象,這時使用identical()函數),如下是work的工做原理:

  1. 若是x或y不是空,若是兩個都是null返回true,若是兩個有一個不是null返回false
  2. 返回方法x.==(y)的結果(運算符如==是在第一個操做數上調用的方法,你甚至能夠覆蓋許多運算符,包括==,正如您在Overridable運算符中看到的那樣。)

這裏有一些使用相等運算符和關係運算符的例子:

assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);
複製代碼

類型測試運算符Type test operators

asisis!運算符在運行時檢查類型很方便。

Operator Meaning
as 類型轉化 (也用於指定 庫的前綴(library prefixes))
is 若是對象具備指定的類型,則爲True
is! 若是對象具備指定的類型,則爲False

obj is T是true,若是obj繼承了由T指定的接口。如,obj is Object始終爲true。

使用as運算符將對象強制轉換爲特定類型。一般,你應該使用as做爲 is測試對象後跟使用對象表達式 的簡寫。例如,如下代碼。

if (emp is Person) {
  // 檢測類型
  emp.firstName = 'Bob';
}
複製代碼

你可使用as運算符縮短代碼

(emp as Person).firstName = 'Bob';
複製代碼

注意:代碼不是等效的。若是emp是非空的或者不是Person,第一個例子(有is)不作事,第二個例子(有as)拋出一個錯誤

賦值運算符Assignment operators

如你所見,你可使用=運算符賦值。要使用??=運算符,僅在是變量爲null的狀況下賦值。

// 值分配給a
a = value;
// 若是b爲null,則將值賦給b;不然,b保持不變
b ??= value;
複製代碼

複合賦值運算符+=,將操做與賦值組合在一塊兒。

= –= /= %= >>= ^=
+= *= ~/= <<= &= |=

如下是複合賦值運算符的工做原理:

符合賦值 等價表達
對於運算符op a op= b a = a op b
例子: a += b a = a + b
var a = 2; // 賦值使用 =
a *= 3; // 賦值和乘法:a = a * 3 
assert(a == 6);
複製代碼

邏輯運算符

可使用邏輯運算符進行運算(反轉或者結合)布爾值。

Operator Meaning
!expr 反轉如下表達式(將false改成true,反之亦然)
|| 邏輯或
&& 邏輯與

如下是使用邏輯運算符的示例:

if (!done && (col == 0 || col == 3)) {
  // ...Do something...
}
複製代碼

位操做與移位運算符Bitwise and shift operators

Operator Meaning
& 按位取與
| 按位取或
^ 按位異或
~expr 一元逐位補碼(0s變爲1s; 1s變爲0s)
<< 左移
>> 右移

這是使用按位和移位運算符的示例:

final value = 0x22;
final bitmask = 0x0f;

assert((value & bitmask) == 0x02); // 與
assert((value & ~bitmask) == 0x20); // 與非
assert((value | bitmask) == 0x2f); // 或
assert((value ^ bitmask) == 0x2d); // 異或
assert((value << 4) == 0x220); // 左移
assert((value >> 4) == 0x02); // 右移
複製代碼

條件表達式Conditional expressions

Dart有兩個運算符,能夠簡明if-else語句的表達式

condition ? expr1 : expr2

若是condition爲true,則計算expr1(並返回其值);不然,計算並返回expr2的值。

expr1 ?? expr2

若是expr1爲非null,則返回其值;不然,計算並返回expr2的值。 當須要根據布爾表達式進行賦值時使用? :

var visibility = isPublic ? 'public' : 'private';
複製代碼

若是布爾值的表達式測試爲null,考慮使用??:

String playerName(String name) => name ?? 'Guest';
複製代碼

前面的示例至少能夠用另外兩種方式編寫,但沒有這麼簡潔

// 稍微長一點的版本使用?:操做符。
String playerName(String name) => name != null ? name : 'Guest';

// 很長的版本使用if-else語句。
String playerName(String name) {
  if (name != null) {
    return name;
  } else {
    return 'Guest';
  }
}
複製代碼

級聯運算符Cascade notation (..)

級聯操做符(..)容許同一個對象進行一系列操做。除了調用函數,你還能夠訪問統一對象上的字段。這節省你寫臨時變量的步驟,是你的代碼更加優美。

querySelector('#confirm') // Get an object.
  ..text = 'Confirm' // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));
複製代碼

首先調用方法,querySelector(),返回一個選擇器對下個。接下來是級聯運算符cascade notation operates在這個選擇器對象上,忽略任何其後操做可能返回的值。

上面的例子等效於

var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));
複製代碼

你也能夠嵌套級聯

final addressBook = (AddressBookBuilder()
      ..name = 'jenny'
      ..email = 'jenny@example.com'
      ..phone = (PhoneNumberBuilder()
            ..number = '415-555-0100'
            ..label = 'home')
          .build())
    .build();
複製代碼

當心返回一個實際對象的函數上使用級聯。例如,下面這個代碼將發生錯誤。

var sb = StringBuffer();
sb.write('foo')
  ..write('bar'); // Error: method 'write' isn't defined for 'void'.
複製代碼

調用sb.write()返回void,你不能使用級聯運算符對void

嚴格來說,級聯」雙點「不是一個運算符。它僅僅是dart語法的一部分。

其餘運算符

在其餘示例中,能夠看到大多數剩餘的運算符:

Operator Name Meaning
() 函數運用運算符 表示函數調用
[] 列表訪問 引用列表中指定索引處的值
. 成員訪問 指表達式的屬性;:foo.bar從表達式foo中選擇
?. 條件的成員訪問 相似於.,但最左邊的操做數能夠爲null;例如:foo?.bar從表達式foo中選擇屬bar,除非foonull(在這種狀況下,foo?.bar的值爲null)

有關.?...運算符的更多信息,查看類Classes

控制流語句

dart中控制流語句:

  • ifelse
  • for 循環
  • whiledo-while 循環
  • breakcontinue
  • switchcase
  • assert

你可使用 try-catchthrow,影響控制流語句如異常Exceptions所述。

If 和 else

dart支持if語句和else語句(可省略else語句),以下個例子所述。

if (isRaining()) {
  you.bringRainCoat();
} else if (isSnowing()) {
  you.wearJacket();
} else {
  car.putTopDown();
}
複製代碼

dart語言不像JavaScript,控制流語句必須使用布爾值,詳情Booleans

For 循環

你可使用for循環迭代:

var message = StringBuffer('Dart is fun');
for (var i = 0; i < 5; i++) {
  message.write('!');
}
複製代碼

Dart中for循環內部的閉包獲取索引的值,避免了JavaScript的陷阱

var callbacks = [];
for (var i = 0; i < 2; i++) {
  callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());
複製代碼
//改寫JavaScript代碼(非dart官方文檔)
var callbacks = [];
for (var i = 0; i < 2; i++) {
  callbacks.push(() => console.log(i));
}
callbacks.forEach((c) => c());
複製代碼

輸出是01,在JavaScript中這個例子將打印兩個2

若是你正在迭代的對象是迭代器,你可使用forEach()方面,若是不知道迭代的數量,使用forEach()是一個好選擇。

candidates.forEach((candidate) => candidate.interview());
複製代碼

像List和Set這樣的可迭代類也支持迭代的for-in形式:

var collection = [0, 1, 2];
for (var x in collection) {
  print(x); // 0 1 2
}
複製代碼

While 和 do-while

while循環在循環以前判斷條件:

while (!isDone()) {
  doSomething();
}
複製代碼

do-while循環後判斷條件:

do {
  printLine();
} while (!atEndOfPage());
複製代碼

Break 和 continue

使用break來中止循環:

while (true) {
  if (shutDownRequested()) break;
  processIncomingRequests();
}
複製代碼

使用continue 跳到下一個循環:

for (int i = 0; i < candidates.length; i++) {
  var candidate = candidates[i];
  if (candidate.yearsExperience < 5) {
    continue;
  }
  candidate.interview();
}
複製代碼

將上面的例子改寫成迭代器Iterable(如列表或集合)

candidates
    .where((c) => c.yearsExperience >= 5)
    .forEach((c) => c.interview());
複製代碼

Switch 和 case

Dart中的switch語句使用==比較整數,字符串或編譯時常量。比較對象必須都是同一個類的實例(而不是其任何子類型),而且該類不能覆蓋==枚舉類型 (Enumerated types) 在switch語句中運行良好。

注意:Dart中的Switch語句適用於有限的環境,例如interpreters(解釋器)或scanners(掃描儀)。

每一個非空case子句以break語句結束。結束非空case子句的其餘有效方法是continue,throw或return語句。

當沒有case子句匹配時,執行default的代碼:

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();
}
複製代碼

如下示例省略了case子句中的break語句,形成了錯誤:

var command = 'OPEN';
switch (command) {
  case 'OPEN':
    executeOpen();
    // ERROR: Missing break

  case 'CLOSED':
    executeClosed();
    break;
}
複製代碼

然而,Dart確實支持空的case子句,容許某種形式的省略

var command = 'CLOSED';
switch (command) {
  case 'CLOSED': // 空case語句,運行下一個case
  case 'NOW_CLOSED':
    // 運行CLOSED和NOW_CLOSED。
    executeNowClosed();
    break;
}
複製代碼

若是您真的想要跳轉下一個case,可使用continue語句和標籤:

var command = 'CLOSED';
switch (command) {
  case 'CLOSED':
    executeClosed();
    continue nowClosed;
  // 在nowClosed case上執行。

  nowClosed:
  case 'NOW_CLOSED':
    // 運行CLOSED和NOW_CLOSED。
    executeNowClosed();
    break;
}
複製代碼

case子句能夠具備局部變量,這些變量僅在該子句的範圍內有效。

Assert

在程序開發期間,使用assert語句——assert(condition,optionalMessage);若是condition的結果爲false,則不會正常運行。

// Make sure the variable has a non-null value.
assert(text != null);

// Make sure the value is less than 100.
assert(number < 100);

// Make sure this is an https URL.
assert(urlString.startsWith('https'));
複製代碼

也能夠自定義錯誤的信息,添加一個字符串做爲第二個參數。

assert(urlString.startsWith('https'),
    'URL ($urlString) should start with "https".');
複製代碼

assert()第一個參數能夠是任意的表達式,若是表達式的結果爲true,程序正常運行。反之,一個異常拋出。

在如下工具和框架的下,assert()的正常運行。

  • Flutter在debug 模式下
  • 開發者工具,例如:dartdevc默認支持。
  • 一些工具,如dartdart2js,經過命令行添加 --enable-asserts

在生產(正式運行)中,assert被忽略,assert不會評估的參數

異常

Dart代碼會拋出和捕捉異常。異常是一種錯誤說明未知的錯誤發生。若是未捕獲異常,異常會被拋出,致使拋出異常的代碼終止執行。

與Java語言相反,Dart所有的意外都爲非檢查異常。methods不必定聲明瞭它們可能拋出的異常,而且不要求捕獲任何異常。

Dart提供了 ExceptionError,以及他們的子類。你能夠定義本身的異常。可是,Dart程序能夠拋出任何非null對象 - 不只僅是Exception和Error對象 - 做爲異常。

Throw

如下是拋出或引起異常的示例:

throw FormatException('Expected at least 1 section');
複製代碼

你也能夠拋出任意對象:

throw 'Out of llamas!';
複製代碼

注意:生產代碼一般會拋出錯誤實現Error或異常Exception的類。

由於拋出異常是一個表達式,因此能夠拋出異常在=>語句中以及容許表達式的任何其餘地方:

void distanceTo(Point other) => throw UnimplementedError();
複製代碼

Catch

捕捉異常,阻止異常繼續傳遞(除非你從新拋出異常)。捕捉異常試你有機會處理它

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  buyMoreLlamas();
}
複製代碼

要處理可能拋出不止一種異常類型的代碼,能夠指定多個catch子句。一個catch子句對應異常對象的類型以後處理異常。若是catch子句未指定類型,則該子句能夠處理任何類型的拋出對象:

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // 一個特殊的異常
  buyMoreLlamas();
} on Exception catch (e) {
  // 還有其餘任何異常
  print('Unknown exception: $e');
} catch (e) {
  // 處理全部沒有指定的類型
  print('Something really unknown: $e');
}
複製代碼

如前面代碼所示,你可使用oncatch或二者。須要指定異常類型時使用on。在異常處理程序須要異常對象時使用catch

你能夠指定一個或者兩個參數給catch(),第一個參數是拋出的異常,第二個是堆棧信息(一個 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 關鍵字能夠 把捕獲的異常給 從新拋出。

void misbehave() {
  try {
    dynamic foo = true;
    print(foo++); // 返回一個錯誤
  } catch (e) {
    print('misbehave() partially handled ${e.runtimeType}.');
    rethrow; // 容許調用者看到異常。
  }
}

void main() {
  try {
    misbehave();
  } catch (e) {
    print('main() finished handling ${e.runtimeType}.');
  }
}
複製代碼

Final

要確保某些代碼執行,不管是否有異常都須要執行。使用 finally語句。若是沒有catch 語句匹配到異常,在final子句運行以後再拋出異常。

try {
  breedMoreLlamas();
} finally {
  // 老是清理,即便拋出異常。
  cleanLlamaStalls();
}
複製代碼

finally 子句在catch子句以後運行:

try {
  breedMoreLlamas();
} catch (e) {
  print('Error: $e'); // 首先處理異常
} finally {
  cleanLlamaStalls(); // 以後清理
}
複製代碼

想了解更多信息,閱讀庫預覽的Exceptions模塊。

classes

Dart語言是一個面向對象的語言,擁有類和基於mixin的繼承。每一個類是類的一個實例,全部的類都來源與Object。基於mixin的繼承意味着儘管每一個類(除了Object)有一個額外的超類,類主體能夠在多個類層次結構中重用。

類成員

Object的成員由函數和數據(方法和實例變量)組成。當你調用一個方法,能夠在對象上調用它:該方法能夠訪問該對象的函數和數據

使用點(.)來訪問實例變量和方法:

var p = Point(2, 2);

// 設置實例變量y的值。
p.y = 3;

// 得到y的值。
assert(p.y == 3);

// 調用p上的 distanceTo() 
num distance = p.distanceTo(Point(4, 4));
複製代碼

使用?.取代.避免因最左邊的變量爲空而引起的異常。

//若是p爲非null,則將其y值設置爲4。
p?.y = 4;
複製代碼

使用構造體

你能夠建立對象使用構造體。構造體的名字能夠是類的名字(ClassName )或者類內的方法(ClassName.identifier)。例如,下面的代碼建立Point對象使用Point()Point.fromJson()構造:

var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
複製代碼

如下代碼具備相同的效果,new爲可選的關鍵字:

var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});
複製代碼

版本說明:新關鍵字在Dart 2中變爲可選。

一些方法提供常量構造器(constant constructors)。在構造函數以前放const建立編譯時常量。

var p = const ImmutablePoint(2, 2);
複製代碼

兩個相同的編譯時常量他們是相同的實例

var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

assert(identical(a, b)); // 他們是相同的實例
複製代碼

在常量上下文中,能夠在構造函數或字面量以前省略const。例如,該代碼建立const的map:

// 這裏由不少的const
const pointAndLine = const {
  'point': const [const ImmutablePoint(0, 0)],
  'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
複製代碼

你能夠省略除第一個外的const關鍵詞

// Only one const, which establishes the constant context.
const pointAndLine = {
  'point': [ImmutablePoint(0, 0)],
  'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};
複製代碼

若是常數構造函數在常數上下文以外沒有const,它會建立一個**很是數(non-constant object)**對象:

var a = const ImmutablePoint(1, 1); // 建立常量
var b = ImmutablePoint(1, 1); // 不建立常量

assert(!identical(a, b)); // 不是同一個實例!
複製代碼

版本說明:在Dart 2的常量上下文中,const關鍵字變爲可選。

獲取對象類型

使用ObjectruntimeType的屬性能夠在運行時返回type對象

print('The type of a is ${a.runtimeType}');
複製代碼

至此,您已經瞭解瞭如何使用類。本節的其他部分將展現如何實現類。

實例變量

如下是聲明實例變量的方法:

class Point {
  num x; // 聲明實例變量x,最初爲null。
  num y; // 聲明y,最初爲null。
  num z = 0; // 聲明y,最初爲null。
}
複製代碼

全部爲初始化的實例變量的初始值爲null。

全部的實例變量隱式的定義了一個getter方法,非final的實例變量也隱式的生成了一個setter方法。詳情Getters 和 setters

class Point {
  num x;
  num y;
}

void main() {
  var point = Point();
  point.x = 4; // 使用x的setter方法。
  assert(point.x == 4); // 使用x的getter方法。
  assert(point.y == null); // 值的默認值爲null
}
複製代碼

若是在變量的聲明處初始化它(不是構造函數或者方法),這個值在建立實例的時候設置。建立實例的時間在構造函數和初始化列表執行以前。

class Point {
  num x, y;

  Point(num x, num y) {
    //有一個更好的方法來作到這一點,請繼續關注。 
    this.x = x;
    this.y = y;
  }
}
複製代碼

this關鍵字引用當前實例。

注意:僅在存在名稱衝突時使用此選項。Dart省略了this

將構造函數參數建立實例變量的模式很是常見,dart語法使其加糖,很是簡單:

class Point {
  num x, y;

  // 用於設定x和y的句法糖
  // 在構造函數體運行以前。
  Point(this.x, this.y);
}
複製代碼

默認構造函數(Default constructors)

若是沒有聲明構造函數,則會有默認的構造函數。默認構造函數沒有參數,在超類(被繼承的類)中調用沒有參數的的構造函數

構造函數不會繼承(Constructors aren’t inherited)

子類不會繼承超類的構造函數。子類沒有定義構造函數,則會由一個只有默認(無參數,無名稱)的構造函數

命名構造函數(Named constructors)

使用命名構造函數爲一個類提供多種構造函數:

class Point {
  num x, y;

  Point(this.x, this.y);

  // 命名構造函數
  Point.origin() {
    x = 0;
    y = 0;
  }
}
複製代碼

構造函數沒有繼承,這意味着超類的命名構造函數不會被子類繼承。若是但願使用超類中定義的命名構造函數建立子類,則必須在子類中實現超類的構造函數。

調用非默認的構造函數(Invoking a non-default superclass constructor)

默認,子類中的構造函數調用超類的未命名的無參數構造函數。超類的構造函數在構造函數體的運行以前被調用。若是使用初始化列表,則在調用超類以前執行。總之,執行順序以下:

  1. 初始化列表
  2. 超類的無參數的構造函數
  3. 主類的無參數構造函數

若是超類不是未命名的無參數構造函數,則必須手動調用超類中的一個構造函數。超類構造函數在冒號(:)以後、構造函數體(若是存在)以前。

在下面的例子中,Employee類的構造函數爲其超類Person調用命名構造函數。

class Person {
  String firstName;

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

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

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




//console
//in Person
//in Employee
複製代碼

由於在調用構造函數以前會計算超類構造函數的參數,因此參數能夠是一個表達式,例如函數調用:

class Employee extends Person {
  Employee() : super.fromJson(getDefaultData());
  // ···
}
複製代碼

警告:超類構造函數的參數不能訪問this。如,參數能夠調用靜態方法,但不能調用實例方法。

初始化列表(Initializer list)

除了調用超類構造函數以外,還能夠在構造函數體運行以前初始化實例變量。用逗號分隔初始化程序。

// 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)');
}
複製代碼

警告:初始化程序的右側無權訪問this

在開發期間,可使用初始化列表中的assert來驗證輸入。

Point.withAssert(this.x, this.y) : assert(x >= 0) {
  print('In Point.withAssert(): ($x, $y)');
}
複製代碼

初始化列表賦值final字段時都駕輕就熟。 下面的示例初始化在初始化列表中三個final。

import 'dart:math';

class Point {
  final num x;
  final num y;
  final num distanceFromOrigin;

  Point(x, y)
      : x = x,
        y = y,
        distanceFromOrigin = sqrt(x * x + y * y);
}

main() {
  var p = new Point(2, 3);
  print(p.distanceFromOrigin);
}
//3.605551275463989
複製代碼

重定向構造函數(Redirecting constructors)

有時構造函數的惟一目的是重定向到同一個類中的另外一個構造函數。重定向構造函數的主體是空的,構造函數調用出如今冒號(:)以後。

class Point {
  num x, y;

  // 該類的主構造函數。
  Point(this.x, this.y);

  // 委託主構造函數。
  Point.alongXAxis(num x) : this(x, 0);
}
複製代碼

常量構造函數(Constant constructors)

若是生成從不不會更改的對象,則可使這些對象成爲編譯時常量。爲此,請定義const構造函數並確保全部實例變量都是final

class ImmutablePoint {
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);

  final num x, y;

  const ImmutablePoint(this.x, this.y);
}
複製代碼

常量構造函數並不老是建立常量。有關詳細信息,請參閱關於實例變量(using constructors)的部分。

工廠構造函數(Factory constructors)

實現一個構造函數並不老是建立其類的新實例時,使用工廠的關鍵字。 例如,一個工廠構造可能從緩存返回一個實例,或者它可能會返回一個子類型的實例。

下面的例子演示了一個工廠構造函數從緩存中返回的對象:

class Logger {
  final String name;
  bool mute = false;

  // _cache是庫私有的
  // 在它的名字前面_
  static final Map<String, Logger> _cache =
      <String, Logger>{};

  factory Logger(String name) {
    return _cache.putIfAbsent(
        name, () => Logger._internal(name));
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}
複製代碼

注意:工廠構造函數無權訪問this

像調用任何其餘構造函數同樣調用工廠構造函數:

var logger = Logger('UI');
logger.log('Button clicked');
複製代碼

方法

方法是爲對象提供行爲的函數。

實例方法

在對象上的實例方法能夠訪問實例變量和thisdistanceTo()方法

下面的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);
  }
}
複製代碼

Getters 和 setters

Getters和Setters是對象屬性的讀寫訪問權限特殊的方法,每個實例變量有隱式的getter方法和若是合適的話還有setter方法。你能夠經過 getters 和 setters建立額外的屬性,使用getset關鍵詞。

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

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

  // 定義兩個計算屬性:右側和底部。
  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);
}
複製代碼

藉助於 getter 和 setter ,你能夠直接使用實例變量,而且在不改變客戶代碼的狀況下把他們包裝成方法。

不管是否顯示定義getter,相似自增(++)的操做符都已以預期方式工做。爲了不產生任何意外的影響,操做符只要調用一次 getter ,就會把他的值存在臨時變量裏。

抽象方法

實例getter和setter能夠是抽象的,定義接口,把實現的方法留給其餘的類。抽象的方法僅存在於 抽象類(abstract classes)中。

用分號;代替方法體時方法變爲抽象。

abstract class Doer {
  // 定義方法和變量

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

class EffectiveDoer extends Doer {
  void doSomething() {
    // 提供一個實現,這裏的方法不是抽象的
  }
}
複製代碼

抽象類

使用abstract修飾符來定義抽象類——類不能被實例化。抽象類對定義接口是有用的,一般還有一些實現。若是你想你的抽象的類是可實例化的,請定義爲工廠構造函數factory constructor

抽象類一般有抽象方法。這是一個聲明具備抽象方法的抽象類的示例:

// 這個類被聲明爲抽象的
// 沒法被實例化
abstract class AbstractContainer {
  // 定義構造函數,字段,方法......

  void updateChildren(); // 抽象方法
}
複製代碼

隱式接口

每一個類都隱式定義了一個接口,每一個類包含類的實例和他的實現的全部接口的實例。若是你想建立一個類A支持B的API,不繼承B的實現,class A 應該implement B的接口。

類經過implement實現一個或者更多的接口,而後提供接口須要的API。

// Person的類,實現了接口greet()
class Person {
  // 在接口中,可是它僅僅對於這個庫可見
  final _name;

  // 不在接口中,由於是一個構造函數
  Person(this._name);

  // 在接口中
  String greet(String who) => 'Hello, $who. I am $_name.';
}

// Person接口的實現
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()));
}
複製代碼

一個類實現多接口的實例:

class Point implements Comparable, Location {...}
複製代碼

繼承類

使用extends建立子類,使用super來引用父級

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

class SmartTelevision extends Television {
  void turnOn() {
    super.turnOn();
    _bootNetworkInterface();
    _initializeMemory();
    _upgradeApps();
  }
  // ···
}
複製代碼

重寫成員

子類能夠重寫實例成員,方法、getters和setters。你可使用@override註解代表你重寫了一個成員

class SmartTelevision extends Television {
  @override
  void turnOn() {...}
  // ···
}
複製代碼

使用協變關鍵字(covariant keyword) 在類型安全的代碼中縮小方法中參數的類型或者縮小實例變量的類型。

覆寫運算符

能夠複寫如下表格的運算符,例如,若是定義Vector類,則能夠定義一個+方法來添加兩個向量。

< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
% >>

注意:您可能已經注意到!=不是可覆寫的運算符。表達式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);

  // 運算符==和hashCode未顯示。有關詳情,請參閱下面的註釋。
  // ···
}

void main() {
  final v = Vector(2, 3);
  final w = Vector(2, 2);

  assert(v + w == Vector(4, 5));
  assert(v - w == Vector(0, 1));
}
複製代碼

若是你覆寫==,你應該也覆寫hashCode的對象。例如重寫==hashCode 實現map鍵(Implementing map keys)。獲取更多的信息,一般,看擴展類(Extending a class)

noSuchMethod()

要在使用不存在的實例和方法時作出檢測或者作出反應,能夠覆蓋noSuchMethod()

class A {
  // 除非你覆寫 noSuchMethod
  // 不存在的成員致使NoSuchMethodError。
  @override
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: ' +
        '${invocation.memberName}');
  }
}
複製代碼

重寫noSuchMethod以後,知足如下條件才能調用未寫的方法:

  • 接收者有dynamic類型
  • 接收者的類型有一個定義了未實現的方法(能夠是抽象方法)和重寫noSuchMethod的方法。

如下是我查閱資料對官方文檔的補充

// 會報錯
class Person {
    @override
     noSuchMethod(Invocation msg) => "got ${msg.memberName} "
      "with arguments ${msg.positionalArguments}";
} 
void main() {
    var person = new Person();
    print(person.missing("20", "Shubham")); 
 }
複製代碼
class Person {
  missing(int age, String name); // 抽象方法
  @override
  noSuchMethod(Invocation msg) => "got ${msg.memberName} "
      "with arguments ${msg.positionalArguments}";
}

main(List<String> args) {
  dynamic person = new Person(); //person可使用var、Person 或者 dynamic
  print(person.missing(20, 'shubham')); // 調用抽象方法
}

複製代碼
class Person {
  missing(int age,String name);  // 抽象方法

  @override // 重寫noSuchMethod
    noSuchMethod(Invocation invocation) => 'Got the ${invocation.memberName} with arguments ${invocation.positionalArguments}';
}

main(List<String> args) {
  dynamic person = new Person(); //person可使用var、Person 或者 dynamic
  print(person.missing(20,'shubham')); // 調用抽象方法
}
複製代碼

獲取更多信息,查看 noSuchMethodui運轉說明(noSuchMethod forwarding specification)

枚舉類型

枚舉類型是一種用於表示固定數量的常量值的一種特殊類的特殊類。

Using enums

使用enum關鍵字聲明枚舉類型:

enum Color { red, green, blue }
複製代碼

每個枚舉有一個indexgetter,調用它能夠返回一個位置的值(0爲基準)。例如第一個index是0,第二個值是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);
複製代碼

能夠在 switch語句(switch statements)中使用枚舉,若是你不處理全部的枚舉值,你會獲得一個警告。

var aColor = Color.blue;

switch (aColor) {
  case Color.red:
    print('Red as roses!');
    break;
  case Color.green:
    print('Green as grass!');
    break;
  default: // 沒有這個你將會獲得一個警告
    print(aColor); // 'Color.blue'
}
複製代碼

枚舉類型有如下限制:

  • 你不能子類化,混合或實現枚舉。
  • 你沒法顯式實例化枚舉。

有關更多信息,請參閱Dart語言規範(Dart language specification)

爲類添加新功能:mixins

混入(Mixins)是一種多個類層次中重用代碼的方法。

使用with關鍵字後跟一個或多個mixin名來使用混入。接下來這兩個類顯示如何使用混入:

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

class Maestro extends Person with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}
複製代碼

建立一個繼承Object的類和不聲明構造函數來實現mixin,除非您但願mixin可用做常規類,不然使用mixin關鍵字而不是class。例如:

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');
    }
  }
}
複製代碼

使用on指定某一種類型(超類是指定的類型)能夠用mixin,這樣你的mixin能夠調用它未定義的方法。

mixin MusicalPerformer on Musician {
  // ···
}
複製代碼

版本差別:Dart2.1引入了關鍵詞mixin,早期版本中的代碼一般使用抽象類。有關2.1 mixin更改的更多信息,請參閱Dart SDK 版本日誌(Dart SDK changelog)2.1 mixin規範(2.1 mixin specification)

類變量和方法

使用static關鍵字實現類範圍的變量和方法。

靜態變量

靜態變量(類變量)是有用的對於類範圍的狀態和常量

class Queue {
  static const initialCapacity = 16;
  // ···
}

void main() {
  assert(Queue.initialCapacity == 16);
}
複製代碼

靜態變量不能被初始化直到他們被使用。

注意:此頁面以下更喜歡lowerCamelCase(駝峯命名)爲常量名的風格指南建議(style guide recommendation)

靜態方法

靜態方法(類方法)不對實例進行操做,所以沒法訪問它。例如:

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);
}
複製代碼

注意:對於經常使用或普遍使用的實用程序和功能,考慮使用頂級函數而不是靜態方法。

可使用靜態方法做爲編譯時常量。能夠將靜態方法做爲參數傳遞給常量構造函數。

範類

若是你看了基礎類型數組List的API文檔,你將會看到List<E>。這個尖括號裏面標記的將List製成通用的參數化的類型——具備正式類型參數的類型。按照慣例,大多數類型變量都有單字母名稱,例如E,T,S,K和V.

爲何使用範類?

範類常常用做安全的類型時必須的,可是他們更有好處:

  • 正確指定泛型類型能夠生成更好的代碼。
  • 可使用泛型來減小代碼重複。

若是你打算讓列表只包含字符串,你能夠聲明爲的List 的(讀取爲「字符串列表」)。接下來的代碼中,編譯器能夠檢測到指定的添加非字符串到列表這種方式是一個錯誤。

//錯誤
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // Error
複製代碼

使用範類的另外一個緣由是減小代碼。範類可讓你有許多類型間有統的個接口和實現,同時仍然利用靜態分析。例如,建立一個接口對caching對象

abstract class ObjectCache {
  Object getByKey(String key);
  void setByKey(String key, Object value);
}
複製代碼

你發現你想要一個指定字符串版本的接口,你能夠建立另一個接口

abstract class StringCache {
  String getByKey(String key);
  void setByKey(String key, String value);
}
複製代碼

以後你發現你先要一個數字的接口,你有了這個想法

範類能夠解決你的問題建立接口,你能夠建立一個帶有類型參數的接口。

abstract class Cache<T> {
  T getByKey(String key);
  void setByKey(String key, T value);
}
複製代碼

在此代碼中,T是替身類型。它是一個佔位符,能夠將其視爲開發人員稍後定義的類型。

使用集合字面量

List,set和map能夠被參數化。參數化的字面量就像您已經看到的字面量同樣,除了添加<type> <keyType, valueType>(對於map)在括號以前。這裏是一些使用字面量類型的例子

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'
};
複製代碼

使用帶有構造函數的參數化類型

當使用構造函數時指定一種或更多種的類型,放在尖括號內<...>就在類名以後

var nameSet = Set<String>.from(names);
複製代碼

如下代碼建立一個具備整數鍵和View類型值的映射:

var views = Map<int, View>();
複製代碼

通用集合及其包含的類型

Dart通用的類型時具體化的,這意味着它們在運行時攜帶其類型信息。例如,您能夠測試集合的類型:

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

注意:與Dart相反,Java中的泛型使用擦除,這意味着在運行時刪除泛型類型參數。在Java中,您能夠測試對象是否爲List,但沒法測試它是否爲List <String>

限制參數化類型

當實現一個範類,你可能想去限制它的參數的類型。你可使用extends

class Foo<T extends SomeBaseClass> {
  // 實如今這兒
  String toString() => "Instance of 'Foo<$T>'";
}

class Extender extends SomeBaseClass {...}
複製代碼

能夠將SomeBaseClass或其任何子類用做通用參數:

var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();// Extender extends SomeBaseClass
複製代碼

也能夠不指定任何通用參數:

var foo = Foo();
print(foo); // Instance of 'Foo<SomeBaseClass>'
複製代碼

指定任何非SomeBaseClass類型都會致使錯誤:

var foo = Foo<Object>();
複製代碼

使用範類方法

最初,Dart的範類支持僅限於類。範類方法是較新的語法,容許在方法和函數上使用類型參數:

T first<T>(List<T> ts) {
  // 作一些初步的工做或錯誤檢查,而後...
  T tmp = ts[0];
  // 作一些額外的檢查或處理...
  return tmp;
}
複製代碼

這裏是範類參數,first (<T>) 容許在多個地方使用類型參數T

  • 函數返回值爲(T
  • 參數類型中(List<T>
  • 局部變量中(T tmp

查看使用範類方法(Using Generic Methods)獲取更多的信息

庫和可見性

importlibrary指令能夠幫助你建立一個模塊化的共享的代碼。庫不只僅提供API,但也有私有元素:如下劃線(_)開頭的標識符僅在庫中可見。每一個Dart應用程序都是一個庫,即便它不使用庫指令。

庫能夠分佈式的使用包 packages

使用庫

使用import指定在一個庫命名空間來引用這個庫。

例如,Dart Web應用程序一般使用dart:html庫,它們能夠像這樣導入:

import 'dart:html';
複製代碼

導入所需的唯一參數是指定庫的URI。對於內置庫,URI具備特殊的dart: scheme。對於其餘庫,可使用文件系統路徑或package:schemepackage:scheme指定包管理器(如pub工具)提供的庫。例如

import 'package:test/test.dart';
複製代碼

注意:URI表明統一資源標識符。 URL(統一資源定位符)是一種常見的URI。

指定庫前綴

若是導入兩個標識符衝突的庫,則能夠爲一個或兩個庫指定一個前綴。例如,若是library1library2都具備Element類,那麼可能具備如下代碼:

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

// 使用 Element 來自於lib1
Element element1 = Element();

// 使用 Element 來自於 lib2.
lib2.Element element2 = lib2.Element();
複製代碼

導入庫的一部分

若是隻想使用一部分庫,則能夠有選擇地導入該庫。例如:

// 導入僅僅foo函數
import 'package:lib1/lib1.dart' show foo;

// 導入除foo之外iade函數
import 'package:lib2/lib2.dart' hide foo;
複製代碼

延遲加載庫

延遲加載(也稱爲延遲加載)容許Web應用程序在須要庫時按需加載庫。如下是可能使用延遲加載的一些狀況:

  • 減小web app的初始化啓動時間
  • 執行A / B測試-嘗試的算法的替代實施方式中;
  • 加載不多使用的功能,例如可選的屏幕和對話框。

只有dart2js支持延遲加載。 Flutter,Dart VM和dartdevc不支持延遲加載。有關更多信息,請參閱issue #33118issue #27776

要延遲加載庫,必須首先使用deferred as導入它。

import 'package:greetings/hello.dart' deferred as hello;
複製代碼

當您須要庫時,使用庫的標識符調用loadLibrary()

Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}
複製代碼

在上面的代碼中,await關鍵字暫停執行,直到加載庫。有關async和await的更多信息,請參閱異步支持(asynchrony support)

能夠在庫上屢次調用loadLibrary()而不會出現問題。該庫只加載一次。

使用延遲加載時,請注意如下幾點:

  • 延遲庫的常量在其做爲導入文件時不是常量。記住,這些常量不存在,直到遲庫被加載完成。
  • 不能在導入文件中使用延遲庫常量的類型。相反,考慮將接口類型移到同時由延遲庫和導入文件導入的庫。
  • Dart隱含調用LoadLibrary()插入到定義deferred as namespace。在調用LoadLibrary()函數返回一個Future。

實現庫

查看Create Library Packages,得到如何實現一個庫的包的建議,包括:

  • 如何組織庫源代碼。
  • 如何使用export 指令。
  • 什麼時候使用part指令。
  • 什麼時候使用library 指令。

異步支持

Dart庫有不少返回Future或Stream對象的函數。這些函數是異步的。它們在設置可能耗時的操做(例如I / O)以後返回,而無需等待該操做完成。

asyncawait關鍵字支持異步編程,使能夠編寫看起來相似於同步代碼的異步代碼。

處理Futures

當你使用Futures的結果時,你有兩個選項:

使用asyncawait的代碼是異步的,可是看起來很像同步代碼。例如,下面是一些使用await等待異步函數結果的代碼:

await lookUpVersion();
複製代碼

要使用await,代碼必須位於async函數中,該函數被標記爲async

Future checkVersion() async {
  var version = await lookUpVersion();
  // 作點什麼
}
複製代碼

注意:雖然一個async函數可能會執行耗時的操做,程序不會等待這些操做。 相反,async函數執行一直到它遇到其第一 await表達式(詳細信息)。 而後它返回一個Future對象,恢復執行所述await表達完成以後。

使用try,catch,最後使用await處理代碼中的錯誤和清除:

try {
  version = await lookUpVersion();
} catch (e) {
  // 對沒法查找版本作出反應
}
複製代碼

能夠在async函數中屢次使用await。例如,如下代碼等待三遍函數結果:

var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
複製代碼

await expression中,這個表達式的值一般是一個Futrue;若是他不是,而後該值將自動包裝在Future中。此Future對象指示返回一個對象的承諾。await expression的值是返回的對象。await expression使執行暫停,直到該對象可用爲止。

**若是在使用await時收到編譯時錯誤,請確保await在異步函數中。**例如使用await在你的app的main()函數,這個main()的函數體必須標記成async

Future main() async {
  checkVersion();
  print('In main: version is ${await lookUpVersion()}');
}
複製代碼

聲明異步函數

異步函數是一個函數,其主體帶有async的修飾符

向函數添加async關鍵字使其返回Future。例如,考慮如下同步函數,該函數返回一個String:

String lookUpVersion() => '1.0.0';
複製代碼

若是將其更改成async函數(例如,因爲未來的實現會很耗時),則返回的值將爲Future:

Future<String> lookUpVersion() async => '1.0.0';
複製代碼

請注意該函數的體並不須要使用Future 的API。 Dart若是有必要創造Future的對象。 若是你的函數不返回一個有用的值,使得它的返回類型 Future<void>

對於交互式介紹如何使用Future,asyncawait,看異步編程代碼實驗室(asynchronous programming codelab).

處理流

從流內獲得一個值,你有兩個選項

注意:在使用wait for以前,請確保它使代碼更易讀,而且確實須要等待全部流的結果。例如,一般不該該爲UI事件偵聽器使用wait For,由於UI框架會發送無窮無盡的事件流。

異步for循環具備如下形式

await for (varOrType identifier in expression) {
  // 每次流發出一個值時執行。
}
複製代碼

expression 表達式的值必須有 類型流,執行過程以下:

  1. 等待流發出一個值
  2. 執行循環體,包含流發出的值
  3. 循環1和2直到流關閉

要中止收聽流,可使用break或return語句,該語句會脫離for循環並註銷流。

若是在實現異步for循環時收到編譯時錯誤,請確保await for位於異步函數中。例如使用await在你的app的main()函數,這個main()的函數體必須標記成async

Future main() async {
  // ...
  await for (var request in requestServer) {
    handleRequest(request);
  }
  // ...
}
複製代碼

有關異步編程的更多信息,請參見庫介紹的 dart:async 部分。

生成器

當須要延遲生成值序列時,請考慮使用生成器函數。 Dart具備對兩種生成器功能的內置支持:

  • Synchronous 生成器:返回一個 Iterable對象
  • Asynchronous 生成器:返回一個 Stream對象

標記函數體爲sync*實現一個synchronous 的生成器函數,並使用yield語句傳遞值:

Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}
複製代碼

標記函數主體標記爲async*,實現asynchronous 生成器函數,並使用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);
  }
}
複製代碼

可調用類

要容許像函數同樣調用Dart類的實例,實現call()方法。在下面的示例中,WannabeFunction類定義了一個call()函數,該函數接受三個字符串並將它們鏈接起來,每一個字符串之間用空格隔開,並附加一個感嘆號。單擊Run執行代碼。

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!
複製代碼

Isolates

大多數計算機,即便在移動平臺上,也有多核CPU。 爲了利用全部這些核心,開發人員傳統上使用併發運行的共享內存線程。 可是,共享狀態併發容易出錯,而且可能致使代碼複雜化。

全部Dart代碼都在隔離區內運行,而不是線程。 每一個隔離區都有本身的內存堆,確保不會從任何其餘隔離區訪問隔離區的狀態。

有關更多信息,請參閱如下內容:

Typedefs

在dart中,函數是對象,就像字符串和數字是對象同樣。typedef或函數類型別名爲函數類型提供了一個名稱,能夠在聲明字段和返回類型時使用該名稱。當函數類型被分配給變量時,typedef保留類型信息。

請考慮如下不使用typedef的代碼:

class SortedCollection {
  Function compare;

  SortedCollection(int f(Object a, Object b)) {
    compare = f;
  }
}

// 初始化, 不執行。
int sort(Object a, Object b) => 0;

void main() {
  SortedCollection coll = SortedCollection(sort);

  // 咱們都知道compare 是一個函數,
  // 可是什麼類型的函數呢?
  assert(coll.compare is Function); 
}
複製代碼

分配f進行compare時,類型信息會丟失。f的類型是 (Object, Object)int(→ 意味着返回的),然而compare 的類型是Function 。若是咱們使用顯式的名字更改代碼並保留類型信息,則開發者和工具均可以使用這些信息。

typedef Compare = int Function(Object a, Object b);

class SortedCollection {
  Compare compare;

  SortedCollection(this.compare);
}

// 初始化,不執行。
int sort(Object a, Object b) => 0;

void main() {
  SortedCollection coll = SortedCollection(sort);
  assert(coll.compare is Function);
  assert(coll.compare is Compare);
}
複製代碼

注意: 目前 typedefs 僅限於函數類型,咱們指望這一點能有所改變。

由於 typedefs 是簡單的別名,因此它提供了一種方法來檢查任何函數的類型。好比:

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!
}
複製代碼

元數據

使用元數據在你的代碼中添加一個附加的信息,元數據聲明開始一個@,接着式對編譯時常量的引用(例如deprecated)或對常量構造函數的調用。

全部Dart代碼均可以使用兩個註釋:@deprecated@override。有關使用@override的示例,請參見 擴展類Extending a class。這是使用@deprecated註釋的示例:

class Television {
  /// _Deprecated: 使用 [turnOn] 代替._
  @deprecated     // 標記爲不推薦,直到下一個版本
  void activate() {
    turnOn();
  }

  /// Turns the TV's power on.
  void turnOn() {...}
}
複製代碼

您能夠定義本身的元數據註釋。這是定義帶有兩個參數的@todo註釋的示例:

library todo;

class Todo {
  final String who;
  final String what;

  const Todo(this.who, this.what);
}
複製代碼

這是使用@todo批註的示例:

import 'todo.dart';

@Todo('seth', 'make this do something')
void doSomething() {
  print('do something');
}
複製代碼

元數據能夠出如今庫,類,typedef,類型參數,構造函數,工廠,函數,字段,參數或變量聲明以前,也能夠出如今導入或導出指令以前。您能夠在運行時使用反射來檢索元數據。

註釋

Dart支持單行註釋,多行註釋和文檔註釋。

單行註釋

單行註釋以//開頭。 Dart編譯器會忽略//和行尾之間的全部內容。

void main() {
  // TODO: refactor into an AbstractLlamaGreetingFactory?
  print('Welcome to my Llama farm!');
}
複製代碼

多行註釋

多行註釋以 /*開頭,以*/結尾。 Dart編譯器將忽略/**/之間的全部內容(除非註釋爲文檔註釋;請參閱下一節)。多行註釋能夠嵌套。

void main() {
  /* * This is a lot of work. Consider raising chickens. Llama larry = Llama(); larry.feed(); larry.exercise(); larry.clean(); */
}
複製代碼

文檔註釋

文檔註釋是與 ////**開始多行或單行註釋。 連續的行上使用 ///有一個多行文檔註釋一樣的效果。

內部文檔註釋,Dart編譯器忽略,除非它放在括號中的全部文本。 使用括號,能夠參考類,方法,字段,頂級的變量,函數和參數。括號中的名稱在文檔程序元素的詞法範圍內解析

下面是與其餘類和論點引用文檔註釋的例子:

/// A domesticated South American camelid (Lama glama).
///
/// Andean cultures have used llamas as meat and pack
/// animals since pre-Hispanic times.
class Llama {
  String name;

  /// Feeds your llama [Food].
  ///
  /// The typical llama eats one bale of hay per week.
  void feed(Food food) {
    // ...
  }

  /// Exercises your llama with an [activity] for
  /// [timeLimit] minutes.
  void exercise(Activity activity, int timeLimit) {
    // ...
  }
}
複製代碼

在生成的文檔中, [food] 變成了指向 Food 類的 API 文檔鏈接。

爲了轉換 Dart 代碼並生成 HTML 文檔,你可使用 SDK 的 文檔生成器。生成文檔的示例,請參閱 Dart API 文檔。關於如何組織你的文檔,請參閱 文檔註釋準則

總結

此頁面總結了Dart語言的經常使用功能。 更多功能正在實施,但咱們預計,他們將不會破壞現有的代碼。 欲瞭解更多信息,請參見 Dart language specificationEffective Dart.。

要了解更多關於Dart的核心庫,看A Tour of the Dart Libraries.

參考連接

dart.dev/guides/lang…

stackoverflow.com/questions/5…

相關文章
相關標籤/搜索