Flutter:Dart 學習筆記

亂寫筆記發表於2020-03-18

工作上的需要,被迫上線學習Flutter,看了下文件和線上的專欄,人老了,怕容易忘記,記錄下筆記,如果有錯誤的地方,煩請糾正。

一、Dart簡介

Dart是Google在2011年10月正式釋出的程式語言,起初的定位是執行在瀏覽器的指令碼語言。是Google大神由於不滿JavaScript開發的程式後期難維護問題,而新研發的一種物件導向程式語言。然而nodejs的出現,JavaScript很快覆蓋了全棧,很多手機應用和桌面應用也成為了JavaScript的宿主容器。這使Dart始終不溫不火,這麼多年來內建Dart VM的瀏覽器廠家也只有Google自己的產品Chrome(現在好像已經移除)。

很多人都有疑問,為什麼JavaScript那麼火,而Dart的開發者那麼稀缺,Flutter會選擇Dart作為基礎語言去研發呢?這不會提高開發者的介入成本,不利於推廣嗎?個人感覺應該以下幾個原因:

  • Dart是Google自主研發產品,可以避免很多類似Oracle和Android的爭議問題。
  • 自主研發語言,可以靈活迭代,為Flutter架構的形成做最有力的支撐。
  • Dart 尋求轉型,想彎道超車進入移動開發的領域

那麼Google選擇了用Dart語言開發Flutter框架,除了我們分析的客觀原因外,Dart本身有哪些特性是值得Flutter去選擇的?

二、Dart特性

  • Dart是宣告式佈局語言,易於閱讀和視覺化,可以不通過視覺化構建器,通過熱過載直接真機除錯。
  • 同時支援JIT(即時編譯,JavaScript,Python)和AOT(執行前編譯,C,C++) Dart在開發階段,使用了JIT編譯,這可以提高它的研發效率,降低研發週期。Flutter最受歡迎的熱過載,正是因為這一特性。而釋出的時候使用AOT,就不用像RN那樣,在JavaScript和原生中建立低效的方法呼叫對映,因此,Dart具有執行速度快,執行效能好的特點。
  • Drat是單執行緒模型,不需要像多執行緒那樣處理資源共享和資料同步問題,這也就意味著一個函式執行,就將執行到函式結束為止,期間不會被其他Dart程式碼所打斷。Dart的Isolate(隔離區)不會共享記憶體,幾個單獨的worker之間,通過事件迴圈在事件佇列中進行通訊(類似Node)。

三、Dart基礎語法

Dart的執行依賴於Dart VM,大家可以通過安裝Dart SDK,來體驗Dart。安裝過程以MacOS為例子,可以通過brew安裝

brew tap dart-lang/dart
brew install dart
複製程式碼

接著在VSCode安裝Dart外掛和code runner外掛即可在vscode中體驗Dart程式設計。

跟很多程式語言一樣,Dart同樣以main函式作為執行入口

void main() {
  print('hello world');
}
複製程式碼

Dart變數

Dart可以通過var關鍵字建立變數,當使用var時,型別是通過編譯器推斷決定.

var name = 'tony';
print(name is String); // true
複製程式碼

在預設情況下,未初始化的變數值為null,所以我們不需要擔心無法判斷一個傳遞過來的變數是undefined還是null(JavaScript的表示淚目),所以不需要各種if else。

var name;
print(name); // null
複製程式碼

Dart型別

Dart是型別安全的語言,一切皆為物件的設計思路,任何型別都是物件型別,都繼承了頂層的Object。類如bool,int,null,函式,都是繼承於Object。

String name;
print(name is Object); // true
bool isPig;
print(isPig is Object); // true
int num;
print(num is Object); //true
void fun() {}
print(fun is Object); //true
複製程式碼
  • 布林型別

    在Dart中,使用bool關鍵字來定義布林型別,而布林型別只有兩個值,true和false,它們都是編譯時常量。

    var c1 = true;
    var c2 = false;
    print(c1 is bool); // true
    print(c2 is bool); // true
    複製程式碼
  • 數值型別

    在Dart中,使用num關鍵字來定義數值型別,num有兩個子類,分別為int代表整數型別和double代表浮點型別

    num n = 10;
    print(n is int); // true
    num m = 1.1;
    print(m is double); // true
    複製程式碼

    除了常見的基本運算子,比如 +、-、*、/,以及位運算子外,你還能使用繼承自 num 的 abs()、round() 等方法,來實現求絕對值、取整的功能。你會發現,這些常見的運算子都繼承於num

  • 字串型別

    在Dart中,String 由 UTF-16 的字串組成。你可以使用${}內嵌表示式在字串中,如果是一個表示符,可以省略{}

    var name = 'chong';
    print('my name is $name'); // my name is chong
    print('toUpperCase: ${name.toUpperCase()}'); //toUpperCase: CHONG
    複製程式碼

    字串的拼接,與JavaScript一樣,Dart用“+”運算子來拼接。

    var x1 = 'chong';
    var x2 = '...';
    print(x1 + x2); // chong...
    複製程式碼

    對與多行字串的拼接,Dart提供了類似JavaScript的模版字串功能。用三個單引號或者三個雙引號宣告

    var str = '''
        my
        name
        is
        chong
      ''';
      print(str); 
      /*
        my
        name
        is
        chong
      */
    複製程式碼
  • 集合型別

    在Dart中,集合型別包含了陣列型別和字典型別,分別對應List和Map,宣告和使用,都跟Javascript差不多

      var name = ['zhangsan', 'lisi'];
      name.add('wangwu');
      name.forEach((f) => print('$f')); // zhangsan lisi wangwu
    
      var map = {"name": 'chong', "sex": 'boy'};
      map['n'] = 'hello';
      map.forEach((k, v) => print('$k: $v')); // name: chong sex: boy n: hello
    複製程式碼

    在定義name時,編譯器會推斷該List為List,所以如果add的是個int或者其他型別,會型別錯誤。同理 map 會被推斷成Map<String, String>,當然,也可以從定義的時候設定型別,而避免推斷錯誤。

      var map = <String, String>{"name": 'chong', "sex": 'boy'};
      map['n'] = 'hello';
      map.forEach((k, v) => print('$k: $v')); // name: chong sex: boy n: hello
    複製程式碼

Dart常量

Dart中定義常量的關鍵字有兩個,const和final,const的使用方法跟JavaScript的一樣。const和final的區別主要是const是在編譯時期已經確定的值,而final可以定義執行時的值,一旦執行結束,即不可改變。

```
  var x = 10;
  var y = 2;
  final z = x / y; 
  print(z); // 5.0
  const h = x / y; 
  print(h); // Error
```
複製程式碼

Dart運算子

Dart的大多數運算子與其他程式語言的類似。有幾個比較特殊的運算子,用來簡化處理null的情況

  • ??運算子,x??y,表示x為空時,取y的值,x非空時,取x的值

      var x;
      var y = 10;
      var z = x ?? y;
      print(z); // 10
    複製程式碼
  • ?.運算子,x?.y 如果x為null,跳過該語句,避免報錯

      var x;
      print(x?.y); // null
    複製程式碼
  • ??=運算子, x??=y,如果x為null,把y賦值給x。如果x不為null,跳過。

      var x = 10;
      var y = 20;
      x ??= y;
      print(x); // 10
    
      var x;
      var y = 20;
      x ??= y;
      print(x); // 20
    複製程式碼

在Dart中,一切皆物件,所以運算子也是物件成員函式中的一部分。當你在某些應用場景需要複寫運算子時,Dart也提供了複寫機制,只要通過 operator 關鍵字複寫即可.

class Demo {
  var x, y;
  Demo(this.x, this.y);
  Demo operator +(Demo z) => Demo(x + z.x, y + z.y);
  bool operator ==(Demo v) => v.x == x && v.y == y;
}

var x = Demo(3, 3);
var y = Demo(2, 2);
var z = Demo(1, 1);
print(x == (y + z)); // true
複製程式碼

四、函式

Dart的函式由返回值、函式名、引數、函式體4部分組成,函式也是物件,它的型別為Function,這也意味著函式可以被定義為變數和作為函式引數。

  bool isCheck(int x, int y) {
    return x == y;
  }

  void printBool(Function check) {
    print('${check(1, 1)}');
  }

  printBool(isCheck); // true
複製程式碼

Dart函式也支援箭頭表示式,使用方法跟Javascript一致。如上面的例子可以簡化為

  bool isCheck(int x, int y) => x == y;

  void printBool(Function check) => print('${check(1, 1)}');

  printBool(isCheck); // true
複製程式碼

作為集大多數語言優點為一身的程式語言,Dart設計非常靈活的函式傳參設計。設計了通過{}指定傳參,設定了通過[]設定可選引數。這也使Dart的程式碼更加簡潔優雅。不會像JavaScript一樣,如果函式需要三個引數,前兩個為空時,必須佔位。下面通過一個例子來記錄一下Dart函式的傳參。

  void demo1({int x, int y}) => print('$x,$y');
  demo1(y: 2); // null,2

  void demo2(int x, [int y]) => print('$x,$y');
  demo2(1); // 1,null
  demo2(1, 2); // 1,2

  void demo3(int x, int y) => print('$x,$y');
  demo3(1, 2); // 1,2
  demo3(1); // Error

  void demo4(int x, {int y}) => print('$x,$y');
  demo4(1); // 1,null
  demo4(1, y: 2); // 1,2
  demo4(y: 2); // Error
複製程式碼

五、類

物件是類的例項,在Dart裡,所有物件都繼承了頂層的Object,Dart中的類定義,例項話,方法引用,跟JavaScript ES6差不多。差別點是Dart中通過 "_" 來表示私有,宣告變數和方法時,在前面加上下劃線,表示為private,沒加的,表示為public。

class Cat {
  var color, tail; // 顏色、尾巴
  Cat(this.color, this.tail);

  printInfo() {
    print('這是一隻${color}毛髮, ${tail}尾巴的貓?');
  }
}

var cat = new Cat('藍色', '長');
cat.printInfo(); // 這是一隻藍色毛髮, 長尾巴的貓?
複製程式碼
  • 建構函式

    Dart中,類的建構函式有同名建構函式和命名建構函式兩種,同名建構函式,如上面程式碼所示,與類同名。而命名建構函式,用以下方式定義

    class Cat {
      var color, tail; // 顏色、尾巴
      Cat(this.color, this.tail);
      Cat.defaut(var color) : this(color, '短'); // 重定向建構函式,用":"符號呼叫
    
      printInfo() {
        print('這是一隻${color}毛髮, ${tail}尾巴的貓?');
      }
    }
    
    var cat2 = Cat.defaut('白色');
    cat2.printInfo(); // 這是一隻白色毛髮, 短尾巴的貓?
    複製程式碼
  • 複用

    在物件導向程式語言中,複用是個很重要的概念,很多程式語言都有繼承承,介面等概念,來實現程式碼的複用。Dart也不例外,Dart中,也引入了繼承,介面的概念。

    class Animal {
      var color, tail;
      Animal(this.color, this.tail);
    
      printInfo() {
        print('這是一隻${color}毛髮, ${tail}尾巴的貓?');
      }
    }
    
    class Cat extends Animal {
      var type = '大型';
      Cat(color, tail, {type}) : super(color, tail);
      @override
      printInfo() {
        print('這是一隻${color}毛髮, ${tail}尾巴的貓?,屬於${type}貓科');
      }
    }
    
    class Dog implements Animal {
      @override
      var color, tail;
      @override
      printInfo() {
        print('這是一隻小狗');
      }
    }
    
    var cat = new Cat("黑色", "長")..printInfo(); // 這是一隻黑色毛髮, 長尾巴的貓?,屬於大型貓科
    var dog = new Dog()..printInfo(); // 這是一隻小狗
    複製程式碼

    除了繼承和介面實現複用外,Dart還提供了Mixins方式。Mixins的中文意思是混入,就是在類中混入其他功能。在Dart中可以使用mixins實現類似多繼承的功能。

    class Poultry {
      printInfo() {
        print('這是一種家畜');
      }
    }
    
    class Big with Poultry {}
    
    var big = new Big().printInfo(); // 這是一種家畜
    複製程式碼

相關文章