Dart語言指南(一)

此文着重展現如何使用Dart語言的每個主要功能,從變量和操做符到類和庫,假設您已經知道如何用另外一種編程語言。javascript

學習更多Dart核心庫,查看Dart庫指南.html

Note: 你可使用DartPad運行大部分功能 .java

不管什麼時候須要有關語言功能的詳細信息,請諮詢Dart語言規範git

一個基本的Dart程序

如下代碼使用了許多Dart最基本的功能:github

// Define a function.
printNumber(num 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.
  printNumber(number); // Call a function.
}

打開Dartpad.web

這個程序使用的方法適用於全部(或幾乎全部)Dart應用程序express

// This is a comment.編程

使用//來進行註釋,也可使用/* */. 更多查看註釋.c#

numapi

一種類型,其餘內置類型,String,int,bool.

42

字面量即常量。

print()

控制檯輸出使用的函數。

'...' (或 "...")

表示字符串。

$variableName (或${expression})

字符串插值:包含字符串文字中的變量或表達式的字符串等價物. 更多信息查看字符串。

main()

應用程序執行的開始函數(特殊,必需). 更多信息查看main()函數

var

一種聲明變量而不指定其類型的方式.

咱們的代碼遵循Dart風格指南中的約定。 例如,咱們使用雙空格縮進。

重要概念

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

  • 你能夠放在變量中的全部東西都是一個對象,每一個對象都是一個類的實例。偶數,函數和null都是對象。全部對象都從Object類繼承。
  • 指定靜態類型(例如上例中的num) (您可能也有興趣有一個特殊的類型:dynamic。)在Dart 1.x中指定靜態類型是可選的,但Dart正在轉向成爲徹底類型的安全語言。
  • 強類型模式下,靜態和運行時檢查確保您的代碼是安全的,幫助您捕獲開發中的錯誤,而不是在運行時。強力模式在Dart 1.x 中是可選的,但在Dart 2.0中不是可選的。
  • Dart在運行它以前解析全部的代碼。能夠向Dart提供提示,例如,使用類型或編譯時常量來捕獲錯誤或幫助您的代碼運行得更快。
  • Dart支持頂層函數(如main()),以及連接到類或對象(分別爲靜態方法和實例方法)的函數。你也能夠在函數內部建立函數(嵌套或局部函數)。
  • 一樣,Dart支持頂級變量,以及一個類或對象的變量(靜態變量和實例變量)。實例變量被稱爲字段或屬性。
  • 與Java不一樣,Dart沒有關鍵字publicprotectedprivate。若是一個標識符如下劃線(_)開頭,則它的庫是私有的。有關詳細信息,請參閱庫和可見性
  • 標識符能夠以字母_開頭,後面是字符數字的任意組合
  • 有時候區分表達式聲明是很重要的,必須搞明白二者的含義。
  • Dart工具能夠報告兩種類型的問題:警告和錯誤。 警告只是代表您的代碼可能沒法正常工做,但它們並不妨礙您的程序執行。 錯誤能夠是編譯時或運行時。 編譯時錯誤會阻止代碼執行; 運行時錯誤致使代碼執行時引起異常
  • Dart 1.x有兩種運行模式:生產production )和檢查checked)。 咱們建議您在檢查模式下進行開發和調試,並部署到生產模式。 生產模式是Dart程序的默認運行模式,針對速度進行了優化。 生產模式會忽略斷言和靜態類型。 檢查模式是一種開發人員友好模式,可幫助您在運行時捕獲某些類型的錯誤。 例如,若是將一個非數字賦給一個聲明爲num的變量,那麼檢查模式會拋出一個異常。

Dart 2.0 注意:在Dart 2.0中除去了檢查模式。 有關更多信息,請參閱Dart 2.0更新日誌

關鍵字

下表列出了Dart語言特別處理的詞語。

上標1內置標識符,若是使用內置標識符做爲類或類型名,將發生編譯錯誤.

上標2是新的, 有限的保留字相關的,Dart的1.0版本後添加異步支持. 在任何標有async, async*,  sync*. 的功能體不能使用async, await, 或yield 做爲標識符,更多信息查看異步支持

關鍵詞表中的都是 保留字. 不能使用這些保留字做爲標識符.

變量

如下是建立變量併爲其分配值的示例:

var name = 'Bob';

變量是引用,名爲name的變量包含一個值爲「Bob」的String對象的引用.

默認值

未初始化的變量的初始值爲null. 即便數值類型的變量最初也爲空,由於數字是對象.

int lineCount;
assert(lineCount == null);
// Variables (even if they will be numbers) are initially null.

Note: 在生產模式下assert()調用被忽略. 在檢查模式下,assert(condition)拋出異常,除非condition 爲 true. 更多查看 Assert 部分.

可選類型

您能夠選擇在變量聲明中添加靜態類型:

String name = 'Bob';

添加類型是清楚表達您的意圖的一種方式。 諸如編譯器和編輯器之類的工具能夠經過提供代碼完成和對錯誤和代碼完成的早期警告來使用這些類型來幫助您.

Note: 此頁面遵循 style guide recommendation建議, 對於局部變量使用var, 而不是類型註釋.

Final 和 const

若是變量的值不發生變化, 那麼就可使用final or const, 而不是var 或其它修飾符. final 修飾的變量只能設置一次值; const修飾的變量應當在聲明處賦值,它是一個編譯時的常數. (const變量是一種隱式的final變量.) 全局final變量或類變量在第一次使用時初始化.

Note: 實例變量能夠是 final 而不能是const.

如下是建立和設置final變量的示例:

final name = 'Bob'; // Or: final String name = 'Bob';
// name = 'Alice';  // Uncommenting this causes an error

使用const修飾的變量做爲編譯時的常量。若是const變量處於類級別使用static const修飾. 在聲明處爲const修飾的變量賦值:

const bar = 1000000;       // Unit of pressure (dynes/cm2)
const atm = 1.01325 * bar; // Standard atmosphere

const 關鍵字不只僅是聲明常量變量. 您還可使用它來create常量值,以及聲明建立常量值的構造函數,任何變量均可以有一個常量值.

// Note: [] 建立一個空列表.
// const [] creates an empty, immutable list (EIA).
var foo = const [];   // foo is currently an EIA.
final bar = const []; // bar will always be an EIA.
const baz = const []; // baz is a compile-time constant EIA.

// You can change the value of a non-final, non-const variable,
// even if it used to have a const value.
foo = [];

// You can't change the value of a final or const variable.
// bar = []; // Unhandled exception.
// baz = []; // Unhandled exception.

更多關於使用 const 建立常量值, 查看 ListsMaps, and Classes.

內置類型

Dart語言對如下類型有特殊的支持:

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

您可使用文字初始化任何這些特殊類型的對象. 例如, 'this is a string' 是一個字符串文字, true 是一個布爾文字.

由於Dart中的每一個變量都指向一個對象 - class的一個實例—一般可使用構造函數來初始化變量. 一些內置類型有本身的構造函數. 例如,您可使用Map() 構造函數建立map對象, 使用代碼 new Map().

數字

Dart中定義了兩種數字類型:

int

整數值應在 -2 53 to 253之間

double

64位(雙精度)浮點數, 由IEEE 754標準規定

int 和double 都是 num的子類. num類型包括基本的運算符,例如+, -, /, and *, 也能夠在其餘方法中找到 abs(),ceil(), 和 floor(),. (等位運算符,如 >>, 在 int 類中定義.) 若是num和它的子類型沒有你尋找的, dart:math 庫可能會有.

警告: 對於在-2 53 到 253 以外的數在javascript與Dart VM環境中運行處理的機制有所不一樣,由於Dart具備任意精度。 查看 issue 1533 獲取更多

Integers 是沒有小數點的數字。下面是一些定義整數文字的例子:

var x = 1;
var hex = 0xDEADBEEF;
var bigInt = 34653465834652437659238476592374958739845729;

若是一個數字包含一個小數,那麼它是一個double。如下是定義雙字面值的一些示例:

var y = 1.1;
var exponents = 1.42e5;

如下是將字符串轉換成數字的方法,反之亦然:

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

類型指定傳統的按位移(<<, >>), AND (&), 和OR (|) 運算符。例如:

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;

Strings

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前綴修飾字符串能夠將轉義符做爲普通字符串:

var s = r"In a raw string, even \n isn't special.";

查看 Runes 瞭解跟多關於解析字符串中轉義字符的信息.

文字字符串是編譯時常量,任何內插的表達式都是一個編譯時常數,能夠計算爲null或 numeric, string, 或 boolean 值.

// 如下變量能夠插入到一個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 = const [1, 2, 3];

const validConstString = '$aConstNum $aConstBool $aConstString';
// const invalidConstString = '$aNum $aBool $aString $aConstList';//出錯

使用strings的更多信息, 查看 Strings and regular expressions.

Booleans

爲了表示布爾值,Dart有一個名爲 bool的類型. 只有兩個對象是bool類型:布爾文字true和false,Only two objects have type bool: the boolean literals true and false, 它們都是編譯時常量。 .

當Dart須要一個布爾值時,只有值 true 被視爲true. 全部其餘值被視爲假。 與JavaScript不一樣,諸如 1"aString", 和 someObject 之類的值都被視爲false.

例如,請思考如下代碼,該代碼既有JavaScript也能夠是Dart代碼:

var name = 'Bob';
if (name) {
  // Prints in JavaScript, not in Dart.
  print('You have a name!');
}

若是您將此代碼做爲JavaScript運行,則會打印「You have a name!」,由於 name 是一個非空對象. 在Dart中的production mode下, 因爲name 轉換爲 false (由於name != true)因此上述代碼根本不打印. 在Dart的checked mode模式下, 前面的代碼拋出一個異常,由於name 變量不是一個bool .

這是另外一個在JavaScript和Dart中行爲不一樣的代碼示例:

if (1) {
  print('JS prints this line.');
} else {
  print('Dart in production mode prints this line.');
  // However, in checked mode, if (1) throws an
  // exception because 1 is not boolean.
}

Note:前兩個例子僅在生產模式下工做,而不是檢查模式。 在檢查模式下,若是在布爾值被指望時使用非布爾值,則拋出異常.

Dart中對Booleans的設計主要是爲了不許多能夠被看做是true的值,對於咱們來講,不能使用if (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

也許在幾乎每一個編程語言中最多見的集合是array或對象的有序組。在Dart中array是 List 對象,因此咱們一般只是調用lists.

Dart列表文字看起來像JavaScript數組文字。 這是一個簡單的Dart列表:

var list = [1, 2, 3];

Lists 使用基於零的索引,其中0是第一個元素的索引,list.length - 1 是最後一個元素的索引。 您能夠像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.

List 類型有許多方便操做列表的方法。 有關列表的更多信息,請參見 Generics 和 Collections.

Maps

map是一種將key和value相關聯的對象,key和value均可以是任何對象,key 不可重複,value value 可重複。dart中支持map字面量和 Map類型來構建map.

這裏有幾個簡單的Dart map,使用map字面量建立:

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

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

你也可使用map構造器建立map對象:

var gifts = new Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';

var nobleGases = new Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';

向現有map對象添加新的鍵值對,就像在JavaScript中同樣:

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

從map對象中獲取值與JavaScript中的值相同:

var gifts = {'first': 'partridge'};
assert(gifts['first'] == 'partridge');

若是調用的key不在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對象,請在map文字以前添加const 修飾:

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

// constantMap[2] = 'Helium'; // Uncommenting this causes an error.

For more information about maps, see Generics and Maps.

Runes

在Dart中,符號是字符串的UTF-32編碼.

Unicode爲全部世界寫做系統中使用的每一個字母,數字和符號定義惟一的數值,由於Dart字符串是UTF-16代碼單元的序列,所以在字符串中表達32位Unicode值須要特殊語法.

表達Unicode代碼點的一般方式是 \uXXXX, 其中XXXX是一個4位數的十六進制值. 例如,心臟字符 (♥) 是 \u2665. 要指定多於或少於4個十六進制數字,請將該值放在大括號中. 例如,笑聲表情符號 (😆) 是 \u{1f600}.

String 類有幾個屬性可用於提取符文信息. codeUnitAt 和 codeUnit 屬性返回16位代碼單元. 使用 runes 屬性來獲取字符串的符文.

如下示例說明了符文,16位代碼單元和32位代碼點之間的關係單擊運行按鈕( ) 以查看運行中的符文

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

Note: 使用列表操做操縱符文時要注意。 根據具體的語言,字符集和操做,這種方法很容易分解。 . 更多信息查看如何反轉Dart中的字符串?在 Stack Overflow 上.

Symbols

Symbol對象表示在Dart程序中聲明的操做符或標識。你可能不會須要使用這些符號,但他們對於由名字指向的API是頗有用的,由於時常要改變的是標識符的名字,而不是標識符的符號.

爲了獲得符號的標識,使用符號的文本,只是 # 後跟標識符:

#radix
#bar

Symbol 是編譯時常量.

關於symbols的更多信息, 查看 dart:mirrors - reflection.

Functions

Dart是一個真正的面嚮對象語言,因此即便函數也是對象,也有一個類型 Function. 這意味着能夠將函數分配給變量或做爲參數傳遞給其餘函數. Y您也能夠調用Dart類的實例,就像它是一個函數同樣. 更多查看 Callable classes.

如下是實現函數的示例:

bool isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

儘管Dart強烈建議使用爲公共API鍵入註解中定義的類型註解,可是你也能夠忽略它:

isNoble(atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

若是Funtion只有一個表達式,你可使用簡潔語法:

bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

=> expr 是 { return expr; }的簡寫形式. => 符號有時又被叫作 胖箭頭表達式.

Note: 僅僅一個 表達式—不是一個語句塊—可以出如今箭頭 (=>) 和分號 (;)之間.好比, 你不能使用 if 語句塊, 但你可使用 條件表達式 .

一個function能夠擁有兩種類型的參數: 必選參數 和 可選參數. 必選參數先列出來, 緊接着是可選參數.

可選參數(Optional parameters)

可選參數分爲 命名參數 和 位置參數 ,一個函數中只能使用其中一中,即不能同時存在於一個函數中。

可選命名參數(Optional named parameters)

當調用一個函數時, 你可使用 參數名的形式指定命名參數. 好比:

enableFlags(bold: true, hidden: false);

當定義函數時, 使用 {param1param2, …} 來指定命名參數:

/// Sets the [bold] and [hidden] flags to the values
/// you specify.
enableFlags({bool bold, bool hidden}) {
  // ...
}

可選位置參數(Optional 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');

參數默認值

函數可使用=來爲可選命名和可選位置參數制定默認值. 默認值必須是編譯時常量. 若是沒有提供默認值,默認值是 null.

下邊爲可選命名參數指定默認值:

/// Sets the [bold] and [hidden] flags to the values you
/// specify, defaulting to false.
void enableFlags({bool bold = false, bool hidden = false}) {
  // ...
}

// bold will be true; hidden will be false.
enableFlags(bold: true);

版本要記: 舊代碼可能使用冒號 (:)代替 = 來設置命名參數的默認值. 緣由是在 SDK 1.21版本之前, 只有: 被命名參數支持. 那種支持接近棄用, 咱們推薦 使用 = 指定默認值, 而且 specify an SDK version of 1.21 or higher.

下面的例子是爲位置參數指定默認值:

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(), 爲 list 參數指定了默認數組, 爲gifts 參數指定默認Map集合.

void doStuff(
    {List<int> list = const [1, 2, 3],
    Map<String, String> gifts = const {
      'first': 'paper',
      'second': 'cotton',
      'third': 'leather'
    }}) {
  print('list:  $list');
  print('gifts: $gifts');
}

main()函數

每個應用必須有一個頂級 main() 函數, 做爲應用執行的起點.main() 函數返回  和一個 可選的List<String> 參數.

web應用中的main() 函數:

void main() {
  querySelector("#sample_text_id")
    ..text = "Click me!"
    ..onClick.listen(reverseText);
}

Note: .. 語句在代碼前被叫作 級聯. 使用級聯, 你能夠對單個對象執行多重操做.

命令行應用中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最爲最好的對象

你能夠爲一個函數傳遞另外一個函數:

printElement(element) {
  print(element);
}

var list = [1, 2, 3];

// Pass printElement as a parameter.
list.forEach(printElement);

你也能夠爲一個變量指派一個函數:

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

這個例子採用了匿名函數.更多信息參考下一節.

匿名函數(Anonymous functions)

大多數函數都被命名, 好比 main() 或 printElement(). 你也能夠建立一個無名函數叫作匿名函數,或是lambda表達式閉包. 你能夠給一個變量指定匿名函數, 你能夠將它添加到集合或移除.

匿名函數看起來與命名函數及其類似— 零個或更多參數, 被逗號和可選的類型修飾符分割, 在圓括號中間. 代碼塊包含在函數體內部:

([[Typeparam1[, …]]) { 
  codeBlock
}; 

下面的匿名函數包含一個未指定類型的參數i. 函數調用list中的每一項, 打印包含值和索引的字符串.

var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums'];
list.forEach((i) {
  print(list.indexOf(i).toString() + ': ' + i);
});

點擊運行按鈕 (  )執行代碼.

void main() {
  var list = ['apples', 'bananas', 'oranges'];
  list.forEach((item) {
    print('${list.indexOf(item)}: $item');
  });
}

若是函數只包含一個語句, 可使用胖箭頭.將如下行粘貼到DartPad中,而後單擊運行以驗證其功能是否相同。

list.forEach((i) => print(list.indexOf(i).toString() + ': ' + i));

語法做用域(Lexical scope)

Dart是一種具有詞法做用域的語言, 意味着變量的做用範圍是固定不變的, 僅僅經過佈局代碼. 你能「向大括號以外追溯」來看一個變量的做用域.

下面是一個嵌套函數和變量在每一級做用域的例子:

var topLevel = true;

main() {
  var insideMain = true;

  myFunction() {
    var insideFunction = true;

    nestedFunction() {
      var insideNestedFunction = true;

      assert(topLevel);
      assert(insideMain);
      assert(insideFunction);
      assert(insideNestedFunction);
    }
  }
}

注意 爲何nestedFunction() 能使用每一級的變量, 始終能訪問第一級的變量.

語法閉包(Lexical closures)

一個閉包是一個可以訪問其詞法做用域內變量的函數對象,甚至當這個函數在其原有做用域以外被調用時.

函數內部會包含在臨近做用域內所定義的變量. 在下例中, makeAdder() 捕獲變量 addBy. 不管函數返回到哪裏,它都存儲着addBy.

/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(num addBy) {
  return (num i) => addBy + i;
}

main() {
  // Create a function that adds 2.
  var add2 = makeAdder(2);

  // Create a function that adds 4.
  var add4 = makeAdder(4);

  assert(add2(3) == 5);
  assert(add4(3) == 7);
}

相等測試函數

測試頂級函數, 靜態方法, 和 實例方法 :

foo() {}               // A top-level function

class A {
  static void bar() {} // A static method
  void baz() {}        // An instance method
}

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 = new A(); // Instance #1 of A
  var w = new 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);
}

返回值

全部函數都有返回值. 若是沒有指定返回值, 語句將返回null; 依賴於函數體.

操做符(Operators)

Dart定義了下表這些操做符. 你能夠複寫這些操做符, 詳情見可覆蓋的操做符.

當使用操做符時, 建立表達式:

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

在 操做符表中, 在一行中的每一個操做符都比它後邊的優先級高. 好比, 乘性運算符 % 比等值運算符==優先, 比邏輯與&&優先. 優先級意味着下邊兩行代碼執行結果相同:

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

// 2: 難以閱讀,可是等價.
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);   // 返回double類型
assert(5 ~/ 2 == 2);    // 返回integer類型
assert(5 % 2 == 1);     // 取餘

print('5/2 = ${5~/2} r ${5%2}'); // 5/2 = 2 r 1

Dart也支持自加和自減.

舉例:

var a, b;

a = 0;
b = ++a;        // b得到a的增量值.
assert(a == b); // 1 == 1

a = 0;
b = a++;        // 在b獲得a的值後,a自增.
assert(a != b); // 1 != 0

a = 0;
b = --a;        // 在b獲得a的值前,a自減.
assert(a == b); // -1 == -1

a = 0;
b = a--;        // 在b獲得a的值後,a自增.
assert(a != b); // -1 != 0

等價和關係運算符

下表列出了等價和關係運算符的含義.

要測試兩個對象x和y是否表明相同的東西,請使用 == 運算符。 (在極少數狀況下,您須要知道兩個對象是否徹底相同的對象,請改用 identical() 函數。)== 操做符使用以下:

  1. 若是 xy 是 null: 若是兩個都爲null返回true, 若是隻有其中一個爲null返回false

  2. x.==(y)返回一個函數調用的結果. (這個調用是正確的,像 == 這樣的運算符其實是由第一個操做數所調用的一個方法。你能夠重寫大部分運算符,包括==, 你能夠在覆蓋操做符中看到.)

如下是使用每一個等價和關係運算符的示例:

assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);

類型測試操做符

asis,和 is! 操做符能夠在運行時檢查類型.

若是obj 實現了T定義的接口,那麼obj is T 返回結果爲true,例如: obj is Object 始終返回true (Object是全部類的父類).

使用as 操做符能夠把一個對象轉換爲特定類型。通常來講,若是在is 測試以後還有一些關於對象的表達式,你能夠把as``當作是is測試的一種簡寫。思考下面這段代碼:

if (emp is Person) { // 類型檢查
  emp.firstName = 'Bob';
}

您可使用as 操做符縮短代碼:

(emp as Person).firstName = 'Bob';

Note: as 和 is 代碼不等同. 若是emp 爲null或不是Person, 第一段代碼(使用 is)不作如何操做; 第二段代碼(使用 as) 拋出一個異常.

賦值運算符

正如你所見,你可使用 =操做符爲變量分配值. 只有爲值爲空的變量分配值時, 可使用??= 操做符.

a = value;   // 爲a分配值
b ??= value; // 若是b爲null,爲b分配值;
             // 不然, b維持原值

複合賦值運算符將操做與賦值相結合.

複合賦值運算符工做原理:

如下示例使用賦值和複合賦值運算符:

var a = 2;           // 使用 = 賦值
a *= 3;              // 賦值和乘操做: a = a * 3
assert(a == 6);

邏輯運算符

你可使用邏輯運算符與布爾表達式組合使用或取反.

下面是使用邏輯操做符的例子:

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

位和位移運算符

在Dart中你能夠操縱數字的各個位。 一般,您將對整數使用這些按位運算符和移位運算符的.

下面是使用位運算符和位移運算符的例子:

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);  // 右位移

條件運算符

Dart中提供了兩個操做符的運算符,讓您簡潔地評估表達式,不然可能須要 if-else語句:

condition ? expr1 : expr2

若是condition 爲true, 執行 expr1 (而且返回它的值); 不然執行expr2並返回它的值.

expr1 ?? expr2

若是expr1非空, 返回它的值; 不然執行expr2並返回它的值.

當你須要基於布爾表達式進行賦值操做時, 能夠考慮使用?:.

var finalStatus = m.isFinal ? 'final' : 'not final';

若是布爾表達式爲了測試是否爲null,能夠考慮使用??.

String toString() => msg ?? super.toString();

上一個例子還有另外兩種實現方式,可是不夠簡潔:

// ?: 操做符使用稍長版本.
String toString() => msg == null ? super.toString() : msg;

//使用if-else 語句 代碼很是長.
String toString() {
  if (msg == null) {
    return super.toString();
  } else {
    return msg;
  }
}

級聯運算符 (..)

級聯(..)容許您對同一對象進行一系列操做。 除了函數調用以外,還能夠訪問該對象上的字段。 這一般能夠節省您建立臨時變量的步驟,並容許您編寫更流暢的代碼。.

思考下列代碼:

querySelector('#button') // 獲得一個對象.
  ..text = 'Confirm'   // 使用它的成員.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));

第一個方法叫, querySelector(), 返回選擇器對象. 級聯符號以後的代碼在此選擇器對象上運行,忽略可能返回的任何後續值.

上一個例子等價於:

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

級聯能夠嵌套. 好比:

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

在返回實際對象的函數上構建級聯關係要注意,例如, 下面的代碼執行失敗:

// 不能運行
var sb = new StringBuffer();
sb.write('foo')..write('bar');

方法sb.write() 調用返回void(空),不能在void上構建級聯關係.

Note: 嚴格來講,級聯的".."符號不是運算符。 它只是Dart語法的一部分。

其它運算符

在其餘示例中,您已經看到大部分剩餘的運算符:

查看更多關於 .?., 和 .. 操做符, 查看 Classes.

控制語句

你可使用下列任何一種控制Dart代碼流:

  • if 和 else

  • for 循環

  • while 和 do-while 循環

  • break 和 continue

  • switch 和 case

  • assert

你也可使用 try-catch 和 throw語句來影響控制流, 詳見 Exceptions.

If 和 else

Dart 支持 if 語句和可選的 else 語句,以下例所示. 也能夠查看條件表達式.

if (isRaining()) {
  you.bringRainCoat();
} else if (isSnowing()) {
  you.wearJacket();
} else {
  car.putTopDown();
}

記住:不像Javascript,Dart將除true以外的全部值都視爲 false. 查看 Booleans 瞭解更多信息.

For 循環

你可使用標準 for 循環實現代碼反覆使用.如:

var message = new 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());

如咱們預期先輸出0 而後輸出1. 相反, 在JavaScript中先打印 2 而後打印2 .

若是你要在可迭代的對象上執行迭代, 你可使用 forEach() 方法. 若是你不須要知道迭代計數器那麼使用forEach() 是一個很好的選擇:

candidates.forEach((candidate) => candidate.interview());

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

var collection = [0, 1, 2];
for (var x in collection) {
  print(x);
}

While and 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();
}

若是你使用 可迭代的對象你可能寫出不同的代碼 像list或set:

candidates.where((c) => c.yearsExperience >= 5)
          .forEach((c) => c.interview());

Switch 和 case

Switch語句在Dart中使用==比較整型, 字符串, 或編譯時常量. 比較的對象必須都是同一個類的實例 (而不是其任何子類型), 並且類沒有複寫==枚舉類型 很是適用於switch 語句.

Note: Dart中的Switch語句是在選擇有限的狀況下使用, 好比翻譯器或者掃描器.

規定:每個非空 case 分句以 break 語句結束,其它被容許可做爲非空 case 分句結束的還有continuethrow, 或 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 causes an exception!!

  case 'CLOSED':
    executeClosed();
    break;
}

然而, Dart支持 case 分句內容爲空, 被容許的一種形式的貫穿:

var command = 'CLOSED';
switch (command) {
  case 'CLOSED': // Empty case falls through.
  case 'NOW_CLOSED':
    // Runs for both CLOSED and NOW_CLOSED.
    executeNowClosed();
    break;
}

若是你想實現貫穿, 可使用 continue 語句和一個標記:

var command = 'CLOSED';
switch (command) {
  case 'CLOSED':
    executeClosed();
    continue nowClosed;
    // Continues executing at the nowClosed label.

nowClosed:
  case 'NOW_CLOSED':
    // Runs for both CLOSED and NOW_CLOSED.
    executeNowClosed();
    break;
}

case 分句能夠擁有局部變量, 而且只能夠在該分句的做用域裏使用.

Assert

若是布爾條件爲假,使用assert語句來中斷正常執行,您能夠在整個指南中找到assert語句的例子。 這裏還有一些:

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

Note: Assert 語句只在檢查模式下執行. 在生產模式下不起做用.

將消息附加到斷言, 添加一個字符串做爲第二個參數.

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

版本要點: 第二個參數在SDK 1.22開始引入.

assert的第一個參數能夠是解析爲布爾值或函數的任何表達式。 若是表達式的值或函數的返回值爲真,則斷言成功並繼續執行。 若是它爲false,則斷言失敗,並拋出異常 AssertionError) .

異常Exceptions

Dart代碼能夠拋出和捕獲異常. 異常表示發生了某些意外的錯誤. 若是異常未被捕獲, 引發異常的巢室將被掛起,而且巢室有 和其程序被銷燬。.

與Java不一樣, Dart中的全部異常都屬於未檢查異常.方法也不聲明拋出什麼異常,你也沒有必要捕獲異常.

Dart提供 Exception 和 Error 類型,以及許多預約義的子類型. 你也能夠定義本身的異常. 可是,Dart程序能夠拋出任何非空對象 - 而不只僅是Exception和Error對象 - 做爲異常。.

Throw

這是一個拋出或 喚起異常的例子:

throw new FormatException('Expected at least 1 section');

你也能夠隨便拋出一個對象:

throw 'Out of llamas!';

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

distanceTo(Point other) =>
    throw new UnimplementedError();

Catch

捕獲異常會阻止異常傳播(除非您從新拋出異常)。 捕獲一個異常讓你可以處理它:

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  buyMoreLlamas();
}

要處理可能引起多種類型異常的代碼,能夠指定多個catch子句。 匹配拋出的對象的類型的第一個catch子句處理異常。 若是catch子句未指定類型,則該子句能夠處理任何類型的拋出對象:

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // A specific exception
  buyMoreLlamas();
} on Exception catch (e) {
  // Anything else that is an exception
  print('Unknown exception: $e');
} catch (e) {
  // No specified type, handles all
  print('Something really unknown: $e');
}

如上述代碼所示,您可使用 on 或 catch 或二者。 當您須要指定異常類型時使用 on 。 當異常處理程序須要異常對象時使用 catch.

你能夠爲catch()指定兩個參數. 第一個是拋出的異常, 第二個是 棧軌跡 ( StackTrace 對象).

...
} on Exception catch (e) {
  print('Exception details:\n $e');
} catch (e, s) {
  print('Exception details:\n $e');
  print('Stack trace:\n $s');
}

若是處理異常的一部分,同時容許它傳播,請使用rethrow 關鍵字.

final foo = '';

void misbehave() {
  try {
    foo = "You can't change a final variable's value.";
  } catch (e) {
    print('misbehave() partially handled ${e.runtimeType}.');
    rethrow; // Allow callers to see the exception.
  }
}

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

Finally

爲確保某些代碼不管有無異常都執行,請使用finally 子句. 若是catch 子句沒有相匹配的異常, 則在finally 子句執行後,異常繼續傳播:

try {
  breedMoreLlamas();
} finally {
  // Always clean up, even if an exception is thrown.
  cleanLlamaStalls();
}

finally 子句應該放在全部 catch 子句以後:

try {
  breedMoreLlamas();
} catch(e) {
  print('Error: $e');  // Handle the exception first.
} finally {
  cleanLlamaStalls();  // Then clean up.
}

學習更多查看 Exceptions 部分.

相關文章
相關標籤/搜索