Dart語法基礎


hello world

        // 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. } 

重要的概念

  • 能賦值給變量的所以東西都是對象,包括 numbers, null, function, 都是繼承自 Object 內置類
  • 盡量給變量定義一個類型,會更安全,沒有顯示定義類型的變量在 debug 模式下會類型會是 dynamic(動態的)
  • dart 在 running 之前解析你的所有代碼,指定數據類型和編譯時的常量,可以提高運行速度
  • dart 提供了頂級函數(如:main())
  • dart 沒有 public、private、protected 這些關鍵字,變量名以"_"開頭意味着對它的 lib 是私有的

變量

沒有初始化的變量都會被賦予默認值 null

        var name = 'Bob'; var unInitializeValue1; //未給初值的變量,默認值為 null Int unInitializeValue2; //即使是Int 型,默認值也是 null 

程序中只當數據類型是為了指出自己的使用意圖,並幫助語言進行語法檢查。但是,指定類型不是必須的

num
  • int 取值范圍:-2^53 to 2^53

          // String -> int var one = int.parse('1'); // String -> double var onePointOne = double.parse('1.1'); // int -> String String oneAsString = 1.toString(); // double -> String 注意括號中要有小數點位數,否則報錯 String piAsString = 3.14159.toStringAsFixed(2); 
string
  • '''...''',"""..."""表示多行字符串
  • r'...',r"..."表示“raw”字符串
  • 用 $ 或 ${} 來計算字符串中變量的值
bool
  • Dart 是強 bool 類型檢查,只有bool 類型的值是true 才被認為是true
list

list 基本和 JavaScript 數組一樣,它的方法如下:

        // 使用List的構造函數,也可以添加int參數,表示List固定長度,不能進行添加 刪除操作 var vegetables = new List(); // 或者簡單的用List來賦值 var fruits = ['apples', 'oranges']; // 添加元素 fruits.add('kiwis'); // 添加多個元素 fruits.addAll(['grapes', 'bananas']); // 獲取第一個元素 fruits.first; // 獲取元素最后一個元素 fruits.last; // 查找某個元素的索引號 assert(fruits.indexOf('apples') == 0); // 刪除指定位置的元素,返回刪除的元素 fruits.removeAt(index); // 刪除指定元素,成功返回true,失敗返回false fruits.remove('apples'); // 刪除最后一個元素,返回刪除的元素 fruits.removeLast(); // 刪除指定范圍元素,含頭不含尾,成功返回null fruits.removeRange(start,end); // 刪除指定條件的元素,成功返回null fruits.removeWhere((item) => item.length >6); // 刪除所有的元素 fruits.clear(); // sort()對元素進行排序,傳入一個函數作為參數,return <0表示由小到大, >0表示由大到小 fruits.sort((a, b) => a.compareTo(b)); 
map

類似 JavaScript map

        // Map的聲明 var hawaiianBeaches = { 'oahu' : ['waikiki', 'kailua', 'waimanalo'], 'big island' : ['wailea bay', 'pololu beach'], 'kauai' : ['hanalei', 'poipu'] }; var searchTerms = new Map(); // 指定鍵值對的參數類型 var nobleGases = new Map<int, String>(); // Map的賦值,中括號中是Key,這里可不是數組 nobleGase[54] = 'dart'; //Map中的鍵值對是唯一的 //同Set不同,第二次輸入的Key如果存在,Value會覆蓋之前的數據 nobleGases[54] = 'xenon'; assert(nobleGases[54] == 'xenon'); // 檢索Map是否含有某Key assert(nobleGases.containsKey(54)); //刪除某個鍵值對 nobleGases.remove(54); assert(!nobleGases.containsKey(54)); 

注:如果定義了一個 map 常量,那么value 也必須是常量

symbol

symbol字面量是編譯時常量,在標識符前面加#。如果是動態確定,則使用Symbol構造函數,通過new來實例化


函數

所有的函數都會有返回值。如果沒有指定函數返回值,則默認的返回值是null。沒有返回值的函數,系統會在最后添加隱式的return 語句。

函數參數

函數可以有兩種類型的參數:

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

注:可選參數必須放在最后

通過【】來表示可選參數

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

還可以設置默認參數值

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

函數還可以作為另一個函數的參數

        printElement(element) {
          print(element); } var list = [1, 2, 3]; // Pass printElement as a parameter. list.forEach(printElement); 

函數可以匿名,但是不像 JavaScript, 匿名函數不用加上 function 關鍵字

        var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums']; list.forEach((i) { print(list.indexOf(i).toString() + ': ' + i); }); 
閉包
        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); } 

運算符

除了常見的,還有如下運算符:

  • is 運算符,a is b,用於判斷 a 對象是否是 b 類的實例,返回 bool 值

  • is!意義與上面相反

  • as 運算符;用於檢查類型

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

如果 emp 為空或者不是 Person 的實例,會拋出異常

  • ??= 運算符

          b ??= value; // 如果 b 為空,把 value 賦值給 b; // 否則,b 不變 
  • ?? 運算符

          String toString() => msg ?? super.toString(); //如果 msg 不為空,返回 msg;否則返回后面的 
  • .. 運算符,把對同一對象的不同操作串聯起來

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

流程控制語句

  • if...else
  • for
  • while do-while
  • break continue
  • switch...case 如果 case 后面有表達式但是沒有 break,會拋出異常
  • assert(僅在checked模式有效),如果條件為假,拋出異常

異常

throw
  • 拋出固定類型的異常:

          throw new FormatException('Expected at least 1 section'); 
  • 拋出任意類型的異常:

          throw 'out of llamas!' 
  • 因為拋出異常屬於表達式,可以將throw語句放在=>語句中,或者其它可以出現表達式的地方:

          distanceTo(Point other) =>
              throw new UnimplementedError(); 
catch
  • 可以通過 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個或2個參數。第一個參數表示:捕獲的異常的具體信息,第二個參數表示:異常的堆棧跟蹤(stack trace)

rethrow

rethrow語句用來處理一個異常,同時希望這個異常能夠被其它調用的部分使用

finally

Dart 的finally用來執行那些無論異常是否發生都執行的操作。


使用new語句來構造一個類。構造函數的名字可能是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); 
  • 使用.來調用實例的變量或者方法。
  • 使用 ?. 來避免左邊操作數為null引發異常。
  • 使用const替代new來創建編譯時的常量構造函數。
  • 兩個使用const構建的同一個構造函數,實例相等。
  • 獲取對象的運行時類型使用:o.runtimeType

所有實例變量會生成一個隱式的getter方法,不是final或const的實例變量也會生成一個隱式的setter方法

構造函數
        class Point { num x; num y; // 推薦方式 Point(this.x, this.y); } 

構造函數不能被繼承

子類不會繼承父類的構造函數。如果不顯式提供子類的構造函數,系統就提供默認的構造函數。

命名構造函數

        class Point { num x; num y; Point(this.x, this.y); // 命名構造函數 Point.fromJson(Map json) { x = json['x']; y = json['y']; } } 

使用命名構造函數可以實現一個類多個構造函數。構造函數不能被繼承,父類中的命名構造函數不能被子類繼承。如果想要子類也擁有一個父類一樣名字的構造函數,必須在子類實現這個構造函數

如果父類不顯式提供無參的非命名構造函數,在子類中必須手動調用父類的一個構造函數。在子類構造函數名后,大括號{前,使用super.調用父類的構造函數,中間使用:分割

        class Person { String firstName; Person.fromJson(Map data) { print('in Person'); } } class Employee extends Person { // 父類沒有無參數的非命名構造函數,必須手動調用一個構造函數 super.fromJson(data) Employee.fromJson(Map data) : super.fromJson(data) { print('in Employee'); } } 

當在構造函數初始化列表中使用super()時,要把它放在最后。

        View(Style style, List children)
            : _children = children,
              super(style) {} 

除了調用父類的構造函數,也可以通過初始化列表 在子類的構造函數體前(大括號前)來初始化實例的變量值,使用逗號,分隔

        class Point { num x; num y; Point(this.x, this.y); // 在構造函數體前 初始化列表 設置實例變量 Point.fromJson(Map jsonMap) : x = jsonMap['x'], y = jsonMap['y'] { print('In Point.fromJson(): ($x, $y)'); } } 
工廠構造函數

當實例化了一個構造函數后,不想每次都創建該類的一個新的實例的時候使用factory關鍵字,定義工廠構造函數,從緩存中返回一個實例,或返回一個子類型的實例

        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);// 命名構造函數 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');// 相同對象直接訪問緩存 } 
方法
Getters and setters

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

抽象類

使用abstract關鍵字定義一個抽象類,抽象類不能實例化。抽象類通常用來定義接口。

隱式接口

每一個類都隱式的定義一個接口,這個接口包含了這個類的所有實例成員和它實現的所有接口

一個類可以實現一個或多個(用,隔開)接口,通過implements關鍵字。

        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來創造子類,使用super來指向父類

枚舉類型

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

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

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

枚舉類型有以下限制:

  • 不能繼承,mixin,或實現一個枚舉。
  • 不能顯式的實例化一個枚舉。

泛型

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

雖然Dart 語言中類型是可選的,但是明確的指明使用的是泛型,會讓代碼更好理解

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

泛型用於List 和 Map 類型參數化

        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' }; 
泛型集合及它們所包含的類型

dart的泛型類型是具體的,在運行時包含它們的類型信息。


庫和可見性

使用import 和 library 指令可以方便的創建一個模塊或分享代碼。一個Dart 庫不僅能夠提供相應的API,還可以包含一些以_開頭的私有變量僅在庫內部可見

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

        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. 

如果只需要使用庫的一部分內容,使用show或hide有選擇的導入。

        // 僅導入foo. import 'package:lib1/lib1.dart' show foo; // 除了foo都導入 import 'package:lib2/lib2.dart' hide foo; 

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

        import 'package:deferred/hello.dart' deferred as hello; greet() async { // 使用await關鍵字暫停執行,直到庫加載 await hello.loadLibrary(); hello.printGreeting(); } 

可以在代碼中多次調用loadLibrary()方法。但是實際上它只會被執行一次。

使用延遲加載的注意事項:

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

異步支持

使用async函數和await表達式實現異步操作。

當需要使用一個從Future返回的值時,有兩個選擇:

  • 使用async和await。
  • 使用Future API。

當需要從一個Stream獲取值時,有兩個選擇:

  • 使用async和異步的循環(await for)。

  • 使用Stream API。
    代碼使用了async和await就是異步的,雖然看起來像同步代碼。

          checkVersion() async { //注意這里 async 在小括號后面,和 JavaScript 不一樣 var version = await lookUpVersion(); if (version == expectedVersion) { // Do something. } else { // Do something else. } } 

給函數添加async關鍵字將使函數返回一個Future類型。

        // 修改前是同步的 String lookUpVersionSync() => '1.0.0'; // 修改后 是異步的 函數體不需要使用Future API // dart會在必要的時候創建Future對象 Future<String> lookUpVersion() async => '1.0.0'; 

在Stream中使用異步循環

         // expression的值必須是Stram類型 await for (variable declaration in expression) { // Executes each time the stream emits a value. } 

異步循環的執行流程如下:

  • 等待 stream 發出數據。
  • 執行循環體,並將變量的值設置為發出的數據。
  • 重復1.,2.直到stream 對象被關閉

注:這個過程類似於 JavaScript 的 Rxjs


可調用類

Dart 語言中為了能夠讓類像函數一樣能夠被調用,可以實現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 }




原文鏈接:https://www.jianshu.com/p/81bbd9b6605c


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM