天天看點

Dart文法學習

寫在前面

最近組裡開始組建flutter學習小組,計劃把flutter結合進項目的架構之中。要學習flutter,dart文法當然必不可少,目前dart文法已經更新到2.5了,為了更好的學習flutter,本人将詳細介紹Dart的文法和特性。

Dart的一些概念

  • 所有能夠使用變量引用的都是對象, 每個對象都是一個類的執行個體。在 Dart 中 甚至連數字、方法和 null 都是對象。所有的對象都繼承于 Object 類。
  • Dart是強類型語言,但類型辨別是可選的,因為Dart可以推斷類型。如果要明确說明不需要任何類型,可以使用特殊類型dynamic辨別
  • Dart 在運作之前會先解析你的代碼。你可以通過使用類型或者編譯時常量來幫助Dart 去捕獲異常以及讓代碼運作的更高效
  • Dart 支援頂級方法 (例如 main()),同時還支援在類中定義函數。 (靜态函數和執行個體函數)。 你還可以在方法中定義方法 (嵌套方法或者局部方法)
  • Dart 還支援頂級變量,以及在類中定義變量(靜态變量和執行個體變量)。 執行個體變量有時候被稱之為域(Fields)或者屬性(Properties)。
  • 和 Java 不一樣,Dart 沒有 public、 protected、 和 private 這些關鍵字。如果一個辨別符以 (_) 開頭,則該辨別符在庫内是私有的。
  • 辨別符可以以字母或者 _ 下劃線開頭,後面可以是其他字元和數字的組合。
  • final的值隻能被設定一次。const是一個編譯時的常量,可以通過const來建立常量值,var n = const[],這裡n還是一個變量,隻是被指派了一個常量值,它還是可以符其他值。執行個體變量可以是final,但不能是const。
  • 沒有初始化的變量都會被賦予預設值 null。
  • Dart 工具可以指出兩種問題:警告和錯誤。 警告隻是說你的代碼可能有問題, 但是并不會阻止你的代碼執行。錯誤可以是編譯時錯誤也可以是運作時錯誤。遇到編譯時錯誤時,代碼将無法執行;運作時錯誤将會在運作代碼的時候導緻一個異常。

關鍵字

Dart文法學習

1 帶有上标 1 的關鍵字是内置關鍵字。避免把内置關鍵字當做辨別符使用。 也不要把内置關鍵字用作類名字和類型名字。 有些内置關鍵字是為了友善把 JavaScript 代碼移植到 Dart 而存在的。 例如,如果 JavaScript 代碼中有個變量的名字為 factory, 在移植到 Dart 中的時候,你不必重新命名這個變量。

2 帶有上标 2 的關鍵字,是在 Dart 1.0 釋出以後又新加的,用于 支援異步相關的特性。 你不能在标記為 async、 async*、或者 sync* 的方法體内使用 async、 await、或者 yield 作為辨別符。

是以其他單詞都是保留詞。 你不能用保留詞作為關鍵字。

變量

1、用var來定義變量,變量的類型可以通過變量值推斷出來,dart文法會自動推斷你這個變量的類型

var str= "hello world"; //String類型
var age = 20; //int類型
var high = 1.70; //double類型
           

但是定義之後變量的類型就不可改變了,例如上文中

var str= "hello world"; //String類型
str=1;//編譯器會提示報錯,因為str被指派之後已經是一個string類型了,不能再指派int類型
           

2、也可以像java中那樣直接定義變量類型

String str = "hello world"; //String類型
int age = 20; //int類型
           

3、如果想動态改變變量類型的,可以用dynamic或Object關鍵字來定義變量

//object
  Object object = "hello world";
  print(object.runtimeType);//String
  print(object);//hello world
  object=1;
  print(object.runtimeType);//int 說明類型是可變的
  print(object);//1
  
  //dynamic
  dynamic mic = "hello world";//編譯時不會揣測資料類型,但是運作時會推斷
  print(mic.runtimeType);//String
  print(mic);//hello world
  mic=1;
  print(mic.runtimeType);//int 說明類型是可變的
  print(mic);//1
           

關于var、object、dynamic具體的差別,可以看我這篇:

Dart文法中dynamic,var,object三者的差別

變量的預設值

變量的預設值如文初所說,沒有初始化的變量都會被賦予預設值 null。這個和JAVA很不一樣,JAVA所有的變量都是有預設值的

int value1;
print(value1);
bool value2;
print(value2);
var value3;
print(value3);
dynamic value4;
print(value4);

輸出結果:
value1 = null
value2 = null
value3 = null
value4 = null
           

final 和 const

如果以後不打算修改一個變量,使用final或者const。一個final變量隻能指派一次;一個const變量是編譯時常量。注意:const變量同時也是final變量,執行個體變量可以為final但不能是consts,實踐一下:

//定義初始化一個變量
  final double number = 1;
  number = 2;
  print(number);
           

上面例子用final修飾number并指派,但number = 1的時候,想重新給number再指派的時候,編譯錯報錯:number,a final variable,can only be set once.,意思和上面所說的一樣就是final變量隻能指派一次!下面改為定義為const來修飾number:

//定義初始化一個變量
  const double number = 1;
  number = 2;
  print(number);
           

同樣number = 1編譯器會報錯Constant variables can not be assigned a value意思是常量值不能指派,上面也說了,因為const變量同時也是final變量。如果const變量在類中,請定義為static const

注意:

它們的差別在于,const比final更加嚴格。final隻是要求變量在初始化後值不變,但通過final,我們無法在**編譯時(運作之前)**知道這個變量的值;而const所修飾的是編譯時常量,我們在編譯時就已經知道了它的值,顯然,它的值也是不可改變的。

int Func() {
  // 代碼
}

final int m1 = 60;
final int m2 = Func(); // 正确
const int n1 = 42;
const int n2 = Func(); // 錯誤
           

内置類型

Dart 内置支援下面這些類型:

numbers

strings

booleans

lists (也被稱之為 arrays)

maps

runes (用于在字元串中表示 Unicode 字元)

symbols

Numbers(數值)

Dart 支援兩種類型的數字:

  • int

    整數值,其取值通常位于 -253 和 253 之間。

  • double

    64-bit (雙精度) 浮點數,符合 IEEE 754 标準。

//執行程式入口
void main(){


  //整形,其取值通常位于-2的53次方到2的53之間。
  num x = 777;
  //浮點數 64位
  x = 777.7;

  int y = 777;
  y = 777.7;       //這一行編譯器會報錯,因為将int型的資料轉為double型

  double n = 77,7;
  d = 77;          //這個地方會報錯,因為将double型的資料轉為int型

  int x1 = 7;
  int x2 = 77;
  int x3 = 777;
  int x4 = 7777;

  print(x1.bitLength); //占了3個bit 相當于00000000 00000111
  print(x2.bitLength); //占了7個bit 相當于00000000 01001101
  print(x3.bitLength); //占了10個bit 相當于00000011 00001001
  print(x4.bitLength); //占了13個bit 相當于00011110 01100001
}

           

上面例子可以看到三個點:

使用num聲明的變量,可以随意的轉換類型,如果使用int或者double明确的聲明,那就不能轉換了

判斷一個int值需要多少位時,可以使用bitLength

Strings

Dart的字元串是一系列UTF-16代碼單元,建立方法如下:

String str1 = "hello world"; //可以使用單引号或雙引号
print(str1);
String str2 = """Hi,
hello world.
"""; //使用帶有單引号或雙引号的三重引号可以建立多行字元串
print(str2);
           

輸出結果為

str1 = hello world
str2 = Hi,
  hello world.
           

booleans:

Dart有一個名為bool的類型,隻有兩個對象具有bool類型:true和false,他們都是編譯時常量。

lists:

和其他程式設計語言常見的集合一樣,Dart中使用的集合是數組或有序的對象組。Dart中數組是List對象。

List arr = ["Bruce", "Nick", "John"];
print("arr = $arr");

           

maps:

Map map = {
"name": "Bruce",
"age": 18,
"high": 1.70
};

print("map = $map");
print("map['name'] = ${map['name']}");

var map1 = {
1: "hi",
2: "hello",
3: "yep"
};
print("map1 = $map1");
print("map1[1] = ${map1[1]}");

           

輸出結果為:

map = {name: Bruce, age: 18, high: 1.7}
map['name'] = Bruce
map1 = {1: hi, 2: hello, 3: yep}
map1[1] = hi

           

runes:

符文是字元串的UTF-32代碼點。在字元串中表示32位Unicode值需要特殊文法,常用方法是 \uXXXX,其中XXXX是4位十六進制值,比如小心心(♥)是\u2665。要指定多于或少于4個十六進制數字,請将值放在大括号中。 比如,微笑(?)是\u{1f600}。

String smile = '\u{1f600}';
print("微笑:$smile");

Runes input = new Runes(
  '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
print(String.fromCharCodes(input));

           

輸出結果為:

微笑:?
♥  ?  ?  ?  ?  ?
           

函數

Dart是一種真正的面向對象語言,是以即使是函數也是對象并且具有類型Function。這意味着函數可以配置設定給變量或作為參數傳遞給其他函數。

定義方法

String getName() {
  return "Bruce";
}
           

如果函數體中隻包含一個表達式,則可以使用簡寫文法

可選參數

Dart函數可以設定可選參數,可以使用命名參數也可以使用位置參數。

命名參數,定義格式如 {param1, param2, …}

// 函數定義
void showDesc({var name, var age}) {
  if(name != null) {
    print("name = $name");
  }
  if(age != null) {
    print("age = $age");
  }
}

// 函數調用
showDesc(name: "Bruce");

// 輸出結果
name = Bruce

           

未知參數,使用 [] 來标記可選參數。

// 函數定義
void showDesc(var name, [var age]) {
  print("name = $name");
  
  if(age != null) {
    print("age = $age");
  }
}

// 函數調用
showDesc("Bruce");

// 輸出結果
name = Bruce

           

預設值

函數的可選參數也可以使用 = 設定預設值

// 函數定義
void showDesc(var name, [var age = 18]) {
  print("name = $name");
  
  if(age != null) {
    print("age = $age");
  }
}

// 函數調用
showDesc("Bruce");

// 輸出結果
name = Bruce
age = 18

           

main函數

和其他程式設計語言一樣,Dart中每個應用程式都必須有一個頂級main()函數,該函數作為應用程式的入口點。

函數作為參數

Dart中的函數可以作為另一個函數的參數。

// 函數定義
void println(String name) {
  print("name = $name");
}

void showDesc(var name, Function log) {
  log(name);
}

// 函數調用
showDesc("Bruce", println);

// 輸出結果
name = Bruce
           

匿名函數

// 函數定義
void showDesc(var name, Function log) {
  log(name);
}

// 函數調用,匿名函數作為參數
showDesc("Bruce", (name) {
    print("name = $name");
  });

// 輸出結果
name = Bruce
           

嵌套函數

Dart支援嵌套函數,也就是函數中可以定義函數。

// 函數定義
void showDesc(var name) {
  print("That is a nested function!");
  
  //函數中定義函數
  void println(var name) {
    print("name = $name");
  }
  
  println(name);
}

// 函數調用
showDesc("Bruce");

// 輸出結果
That is a nested function!
name = Bruce
           

運算符

下面就對一些對于Java來說未使用過的運算符通過代碼來做個介紹。

  • ?.的使用
//定義類
class Person {
  var name;
  Person(this.name);
}

// 調用
Person p;
var name = p?.name; //先判斷p是否為null,如果是,則name為null;如果否,則傳回p.name值
print("name = $name");

// 輸出結果
name = null
           
  • ~/的使用
// 代碼語句
var num = 10;
var result = num ~/ 3; //得出一個小于等于(num/3)的最大整數
print("result = $result");

// 輸出結果
result = 3
           
  • as的使用,as用來做類型轉化
// 類定義
class Banana {
  var weight;
  Banana(this.weight);
}

class Apple {
  var weight;
  Apple(this.weight);
}

// 調用
dynamic b = Banana(20);
(b as Banana).weight = 20; // 正常執行
print("b.weight = ${(b as Banana).weight}");
(b as Apple).weight = 30; // 類型轉換錯誤,運作報錯
print("b.weight = ${(b as	Apple).weight}");

//輸出結果
b.weight = 20
Uncaught exception:
CastError: Instance of 'Banana': type 'Banana' is not a subtype of type 'Apple'

           
  • is的使用
// 函數和類代碼定義
getFruit() => Banana(20); // 擷取一個水果對象

class Banana {
  var weight;
  Banana(this.weight);
}

class Apple {
  var color;
  Apple(this.color);
}

// 調用
var b = getFruit();
if(b is Apple) { //判斷對象是否為Apple類
  print("The fruit is an apple");
} else if(b is Banana) { //判斷水果是否為Banana類
  print("The fruit is a banana");
}

// 輸出結果
The fruit is a banana

           
  • ??的使用
// 操作代碼塊
String name;
String nickName = name ?? "Nick"; //如果name不為null,則nickName值為name的值,否則值為Nick
print("nickName = $nickName");
  
name = "Bruce";
nickName = name ?? "Nick"; //如果name不為null,則nickName值為name的值,否則值為Nick
print("nickName = $nickName");
  
// 輸出結果
nickName = Nick
nickName = Bruce 

           
  • …的使用,級聯操作允許對同一個對象進行一系列操作。
// 類定義
class Banana {
  var weight;
  var color;
  Banana(this.weight, this.color);
  
  void showWeight() {
    print("weight = $weight");
  }
  
  void showColor() {
    print("color = $color");
  }
}

// 調用
Banana(20, 'yellow')
    ..showWeight()
    ..showColor();
    
// 輸出結果
weight = 20
color = yellow

           

控制流語句

Dart中的控制流語句和其他語言一樣,包含以下方式

if and else

for循環

while和do-while循環

break和continue

switch-case語句

以上控制流語句和其他程式設計語言用法一樣,switch-case有一個特殊的用法如下,可以使用continue語句和标簽來執行指定case語句。

var fruit = 'apple';
switch (fruit) {
  case 'banana':
    print("this is a banana");
    continue anotherFruit;
      
  anotherFruit:
  case 'apple':
    print("this is an apple");
    break;
}

// 輸出結果
this is an apple
           

Dart是一種面向對象的語言,具有類和基于mixin的繼承。同Java一樣,Dart的所有類也都繼承自Object。

構造函數

Dart的構造函數同普通函數一樣,可以定義無參和有參,命名參數和位置參數,可選參數和給可選參數設定預設值等。Dart的構造函數有以下幾個特點:

  1. 可以定義命名構造函數
  2. 可以在函數體運作之前初始化執行個體變量
  3. 子類不從父類繼承構造函數,定義沒有構造函數的子類隻有無參無名稱的構造函數
  4. 子類定義構造函數時預設繼承父類無參構造函數,也可繼承指定有參數的構造函數;

命名構造函數和函數體運作前初始化執行個體變量

// 類定義
class Tree {
  var desc;
  
  // 命名構造函數
  Tree.init() {
    desc = "this is a seed";
  }
  
  // 函數體運作之前初始化執行個體變量
  Tree(var des) : desc = des;
}

// 構造函數調用
Tree t = Tree.init();
print("${t.desc}");

Tree t1 = Tree("this is a tree");
print("${t1.desc}");

// 輸出結果
this is a seed
this is a tree

           

構造函數繼承

// 類定義
class Fruit {
  Fruit() {
    print("this is Fruit constructor with no param");
  }
  
  Fruit.desc(var desc) {
    print("$desc in Fruit");
  }
}

class Apple extends Fruit {
  Apple():super() {
    print("this is Apple constructor with no param");
  }
  
  // 預設繼承無參構造函數
  Apple.desc(var desc) {
    print('$desc in Apple');
  }
}

// 構造函數調用
Apple();
Apple.desc("say hello");
  
// 輸出結果
this is Fruit constructor with no param
this is Apple constructor with no param
this is Fruit constructor with no param
say hello in Apple
           

mixin繼承

mixin是一種在多個類層次結構中重用類代碼的方法。

// 類定義
class LogUtil {
  void log() {
    print("this is a log");
  }
}

class Fruit {
  Fruit() {
    print("this is Fruit constructor with no param");
  }
}

class Apple extends Fruit with LogUtil {
  Apple():super() {
    print("this is Apple constructor with no param");
  }
}

// 調用
Apple a = Apple();
a.log(); //可執行從LogUtil繼承過來的方法

// 輸出結果
this is Fruit constructor with no param
this is Apple constructor with no param
this is a log
           

泛型

Dart同Java一樣,也支援泛型。

// 類定義
class Apple {
  var desc;
  Apple(this.desc);
  
  void log() {
    print("${this.desc}");
  }
}

class Banana {
  var desc;
  Banana(this.desc);
  
  void log() {
    print("${this.desc}");
  }
}

class FruitFactory<T> {
  T produceFruit(T t) {
    return t;
  }
}

// 調用
FruitFactory<Banana> f = FruitFactory<Banana>();
Banana b = f.produceFruit(Banana("a banana"));
b.log();
  
FruitFactory<Apple> f1 = FruitFactory<Apple>();
Apple a = f1.produceFruit(Apple("an apple"));
a.log();
  
// 輸出結果
a banana
an apple  
           

最後

本來是想自己玩玩全全實踐一遍的,但是感覺真的好多啊(PS一開始我是想純自己寫的,但是後面。。。你們還是懂的,感謝第一第二位老哥),具體使用還是等項目開發中慢慢實踐吧,個人感覺有java和kotlin或者js基礎的話,分分鐘就會了,感覺像是三者的結合體

參考:

https://juejin.im/post/5c52a386f265da2de25b5c36#heading-22

https://juejin.im/post/5c44727df265da611c274087 【author 真丶深紅騎士】

http://dart.goodev.org/guides/language/language-tour 【dart中文社群】

繼續閱讀