秀髮去無蹤之你不得不知道的Dart

快狗叫車前端團隊發表於2019-09-06

引言

最近flutter發展的如火如荼,而Dart作為flutter的開發語言,瞭解一下伐?

秀髮去無蹤之你不得不知道的Dart

語言特徵

dart 中一切皆物件 dart這個特徵和java很像,所有變數、方法都是物件,那怕僅僅是個數字或字串

入口函式

在dart專案中,必須宣告一個入口函式main,這也很java,程式從main函式開始執行(習慣使用js的需要注意這一點)。

void main(){
  print('hello dart!')
}
複製程式碼

變數宣告

  • 一般宣告變數必須明確型別,型別明確後不能改變,未賦值預設值都是null
    int i = 2;
    i = '3' ; // error:i為int型別,不能賦值字串型別
    複製程式碼
  • var 可以宣告任意型別的變數,賦值後型別不能再改變
    var i;
    i = 2; 
    i = 3;
    i = '4'; // error: var 變數賦值後,型別不能再改變
    複製程式碼
  • dynamic 可以宣告動態型別的變數,每次賦值型別不受限制
    dynamic d = 1;
    d = '123'; // 變數型別確定後還可以再賦值其他型別
    複製程式碼
  • final 變數只能賦值一次,宣告時必須初始化
    final a = 1;
    a = 2; // error: final變數只能賦值一次,再次賦值報錯
    
    final b = [2];
    b[0] = 1;
    b.add(2); // final 雖然不能再次賦值,但是可以改變他成員變數的值
    複製程式碼
  • const 變數是編譯時常量,是一種特殊的final變數
    const a = 1;
    a = 2; // error: const變數只能在初始化時賦值
    
    const b = [2];
    b.add(2); // error: const 變數中的成員變數也不可再次改變
    複製程式碼

方法

  • 一般定義方法須明確入參個數、型別及出參的型別
    // 方法定義,明確入參和出參,編譯時可以做型別校驗,提前發現一些錯誤
    int add(int m, int n) {
      return m + n;
    }
    void main() {
      int a = add(50, 30);
      String b = add('50', 30); // error: 入參型別和出參型別不正確,編譯不通過
    }
    複製程式碼
  • 如果不明確定義方法入參和出參引數型別,編譯時無法進行型別檢查
    // 出參和入參的型別可以忽略,如果忽略,編譯時無法進行型別檢查
    add( m, n) {
      return m + n;
    }
    void main() {
      int a = add(50, 30);
      String b = add('50', 30); // 可以通過編譯,但在執行時會報異常
    }
    複製程式碼
  • 方法入參也可以使用基於命名或位置的可選引數
    // 可選位置引數
    // 可選引數後面不能再有引數
    int add(int m, [int n]) {
      int sum = m;
      if (n != null) {
        sum += n;
      }
      return sum;
    }
    
    void main() {
      print(add(1));
      print(add(1, 2));
    }
    複製程式碼
    // 可選命名引數
    int add({int m, int n}) {
      int sum = 0;
      if (m != null) {
        sum += m;
      }
      if (n != null) {
        sum += n;
      }
      return sum;
    }
    
    void main() {
      print(add());
      print(add(m: 1));
      print(add(n: 2));
      print(add(m: 1, n: 2));
    }
    複製程式碼
  • 定義方法入參時可以定義可選引數的預設值,預設值必須是編譯時常量
    // 預設引數值
    // 預設值只能給可選引數設定
    // 位置可選引數匹配順序是從左到右的,即便有預設引數值
    int add(int m, [int n = 0, int i]) {
      var sum = m + n;
      if (i != null) {
        sum += i;
      }
      return sum;
    }
    
    void main() {
      print(add(1));
      print(add(1, 2));
    }
    複製程式碼
  • 箭頭函式只能跟一個表示式,不能使用語句和程式碼塊(前端開發人員要特別注意,和js的箭頭函式不同)
    // => 函式
    void main() {
      var add = (int m, int n) => m + n;
      var sub = (int m, int n) => return m - m; // error: ruturn 是程式碼語句,箭頭函式不能使用程式碼語句
      add(1 , 2);
    }
    複製程式碼
  • 當方法作為引數傳遞給高階方法時,可以使用匿名方法
    // 匿名函式
    void main() {
      var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums'];
      list.forEach((i) {
        print(list.indexOf(i).toString() + ': ' + i);
      });
    }
    複製程式碼
    也可以使用箭頭函式為高階方法傳參
    // 匿名函式
    void main() {
      var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums'];
      // 轉換為箭頭函式時,要去掉語句後面的分號,
      // 有分號就是語句,沒有分號就變成了表示式,符合箭頭函式要求
      list.forEach((i) => {print(list.indexOf(i).toString() + ': ' + i)});
      // 或
      list.forEach((i) => print(list.indexOf(i).toString() + ': ' + i));
    }
    複製程式碼
  • Dart是靜態作用域語言,編譯時變數的作用域就已經確定(這點也和js不同,前端開發人員需注意)
    // 靜態作用域
    int a = 5;
    class Test {
      int a = 6;
      int getA() {
        return a;
      }
    }
    
    void main() {
      var obj = new Test();
      print(obj.getA()); // 6
      var fun = obj.getA;
      print(fun()); // 6
    }
    複製程式碼

操作符

  • 除法( / ), 整除(~/)
    void main() {
      print('5/2 = ${5 / 2}'); // 5/2 = 2.5
      print('5~/2 = ${5 ~/ 2}'); // 5~/2 = 2
    }
    複製程式碼
  • 型別轉換(as), 型別判斷(is、is!), as 只能從父型別轉子型別, is 判斷變數是某種型別, is!和is相反,判斷變數不是某種型別
    void main() {
      num a = 123;
      //  print(a.isOdd); // error: isOdd 不是num的成員變數
      print((a as int).isOdd);// isOdd 是int的成員方法,所有a必須轉換成int型別後再呼叫
      print('a is not String ? ${a is! String}'); // a is not String ? true
      print('a is int ? ${a is int}'); // a is int ? true
    }
    複製程式碼
  • ??= 為值為null的變數賦值
    void main() {
      var a;
      a ??= 1; // 賦值前a = null,賦值後 a=1
      a ??= 2; // 賦值前 a = 1, 賦值後 a=1
      print(a); // 1
    }
    
    複製程式碼
  • expr1 ?? expr2, 等價於 expr1 != null ? expr1 : expr2
    void main() {
      var a;
      var b = 1;
      print(a ?? b); // 1
      a = 0;
      print(a ?? b); // 0
    }
    複製程式碼
  • 級聯操作(..)可以在同一個物件上連續呼叫多個函式及訪問成員變數
    //  '..'不同於'.',不要求函式有返回值,'..'相當於普通函式執行後返回‘this’
    class Car {
      void move(String direction, double distance) {
        print('小汽車向${direction}行駛了${distance} 千米');
      }
    
      Car run(String direction, double distance) {
        print('小汽車向${direction}行駛了${distance} 千米');
        return this;
      }
    }
    
    void main() {
      Car car = new Car();
      car..move('東', 3)..move('南', 4)..move('西', 4)..move('北', 4);
      car..move('東', 3)..run('西', 3)..run('北', 4).run('西', 4).run('北', 4);
    }
    複製程式碼
  • a?.b 如果a為null,則返回null,否則返回a.b
    void main() {
      int a;
      // print(a.isOdd); // error: a未賦值,不能訪問成員變數和成員函式
      print(a?.isOdd);
      // 相當於
      if (a == null) {
        print(null);
      } else {
        print(a.isOdd);
      }
    }
    複製程式碼

  • 每個物件都是一個類的例項,所有的類都整合於Object
    void main() {
      int a = 1;
      print(a is Object); // true
    }
    複製程式碼
  • 每個類都只能有一個父類,但是可以同時實現多個介面,一個父類可以同時被多個子類繼承
    class Tiger extends Animal implements Fly, Swim {
      // todo
    }
    class Dog extends Animal {
      // todo
    }
    複製程式碼
  • 每個例項變數都會自動生成一個getter方法,Non-final例項變數還會自動生成一個setter方法
    class Test {
      int a;
      // 相當於
      // int _a;
      // int get a => _a;
      // set a(int val) => this._a = val;
    }
    複製程式碼
  • 定義一個和類名字一樣的方法就定義了一個建構函式,還可以帶有其他可選的識別符號,如果沒有定義,會生成一個不帶引數的預設建構函式,建構函式不會被繼承
    class Parent {
      String name;
      Parent(this.name);
    }
    
    class Child extends Parent {
      Child(String name) : super(name);
      Child.create(String name) : super(name);
    }
    void main(){
      var zhangsan = Child('張三');
      var lisi = Child.create('李四');
    }
    複製程式碼
  • 可以使用abstract來定義抽象類,抽象類不能被例項化。抽象類通常含有抽象方法,抽象方法只有方法簽名,沒有實現。
    abstract class Animal {
      // 抽象方法
      void moving();
    }
    複製程式碼
  • 用extends來實現繼承,用implements來實現介面,使用@override來重新父類方法
    class Animal {
      final String name;
      Animal(this.name);
    
      void moving() {
        print('${this.name}正在移動');
      }
    }
    
    abstract class Fly {
      // 抽象方法
      void fly();
    }
    
    class Tiger extends Animal implements Fly {
      Tiger() : super('老虎');
      @override
      void fly() {
        print('如虎添翼');
      }
    }
    複製程式碼
  • 用static定義靜態變數和靜態方法
    class Calculator {
      static int Add(int m, int n) {
        return m + n;
      }
    }
    void main(){
      print(Calculator(1, 2));
    }
    複製程式碼

庫的使用

  • 通過import來引入庫
  • 引用內部庫 import 'dart:schema',如:import 'dart:io';
  • 引用外部庫 import 'package:schema', 如:import 'package:utils/utils.dart';
  • 可以通過 as 來指定庫字首,如 import 'package:utils/utils.dart' as utils;
  • 可以通過 showhide 來部分匯入庫
  • 可以通過 deferred as 延遲載入一個庫

dart 核心庫

  • dart:core 提供了基礎型別、集合和其他少量但是非常核心的功能,dart應用會自動匯入這個庫。
  • dart:convert 提供了一些用來轉換 JSON 和 UTF-8 的轉換器,還可以自定義 新的轉換器。
  • dart:async 非同步程式設計通常使用回撥函式,但是 Dart 提供了另外的 選擇: Future 和 Stream 物件。
  • dart:html 為基於web的應用提供了操作Dom元素和使用HTML5 API的功能。
  • dart:math 提供了常用的數學運算功能。
  • dart:io 提供了一下檔案讀寫、程式訪問、sockets等相關的功能,只有命令列應用可以使用。

PUB

pub類似於node的npm,用於管理dart應用的包,詳情參見pub.dev/

總結

瞭解了上面這些基礎語法,基本上就可以看懂一些dart示例和原始碼了,想有所成,瞭解這些還是遠遠不夠的,但是這些可以作為一個開始,作為學習dart的起點。

參考

關於我們

快狗叫車前端團隊專注前端技術分享,定期推送高質量文章,歡迎關注點贊。
文章同步釋出在公眾號喲,想要第一時間得到最新的資訊,just scan it !

公眾號二維碼

相關文章