Dart語言——45分鐘快速入門(上)

程式設計之路從0到1發表於2019-05-15

前言

谷歌推出Flutter跨平臺UI框架後,對移動端的開發又產生了新的影響,Flutter採用Dart語言開發,而Flutter為什麼選擇Dart語言作為唯一的開發語言呢?總的來說,其擁有如下優勢

  • Dart可基於AOT(Ahead Of Time)編譯,即編譯成平臺的原生程式碼,執行效能高。
  • Dart也可基於JIT(Just In Time)編譯,編譯快速,可熱載入,使開發週期加倍提升(Flutter亞秒級有狀態熱過載)
  • Dart可以更輕鬆地建立以60fps執行的流暢動畫和轉場。Dart在沒有鎖的情況下進行物件分配和垃圾回收
  • Dart語法結合Java與JavaScript語法特點,幾乎沒有令人不適的怪異語法,使Java程式設計師倍感親切,快速上手

通常來說一門語言要麼使用AOT編譯,編譯慢,開發效率低,或者使用JIT編譯,在執行時編譯,雖然可以熱過載,但是執行效率低,而Dart在這兩種之間做出了完美平衡,當開發時使用JIT編譯,除錯快,所見即所得,開發效率高,當釋出時,使用AOT編譯,編譯成目標平臺的原生程式碼,執行效率高。

環境準備

安裝Dart SDK

官方下載地址 共有三種SDK版本選擇

  • Flutter
  • Web
  • Server

本章僅作為Dart程式設計語法學習,這裡建議安裝Server版的SDK,然後選擇Windows版本進行下載。

配置環境變數

在Windows上,通過點選下一步即可安裝,安裝完成後,需將dart-sdk下的的bin目錄新增到系統Path環境變數中。這一步驟是通常的命令配置步驟。

配置 VSCode 編輯器

作為Dart語言的學習,不建議下載笨重的IDE,官方提供支援VSCode 編輯器外掛,建議使用VSCode 學習。

官網下載 VSCode編輯器,安裝完成後,啟動VSCode並在外掛商店中搜尋Dart進行外掛安裝。

測試環境

在VSCode中新建一個test.dart檔案,編寫如下程式碼

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

執行後成功在控制檯輸出hello world!

基礎語法

程式碼註釋

Dart中的程式碼註釋基本與Java語言相同

// 單行註釋

/*
 * 多行註釋
 */

/**
 * 文件註釋
 */

/// 使用三個斜槓開頭
/// 這是Dart特有的文件註釋
複製程式碼

內建資料型別

在這裡插入圖片描述

在Dart中,所有能夠使用變數引用的都是物件,每個物件都是一個類的例項。數字、函式和 null 也都是物件。所有的物件都繼承於Object類。

要注意,沒有初始化的變數預設值為 null。數值型別變數的預設值也是 null

數值型別num有兩個具體子類,分別為intdouble,其中int為整數值,範圍是-2^532^53之間;double則是64位的雙精度浮點數。

變數與常量

定義變數

Dart中定義變數有兩種方式,一種是靜態型別語言常用的方式,顯式指定變數型別,另一種則是動態語言的常用方式,不指定型別,由vm自動推斷。

// 1.通過顯式指定型別來定義變數
String name = "張三";
num age = 18;

// 2.使用關鍵字var,不指定型別
var address = "深南大道";
var id = 100;

/* 使用var定義變數,即使未顯式指定型別,一旦賦值後型別就被固定
 * 因此使用var定義的變數不能改變資料型別
 */
var number = 19;
// 以下程式碼錯誤,無法執行,number變數已確定為int型別
number = "2019";
複製程式碼

如想動態改變變數的資料型別,應當使用dynamicObject來定義變數。

// dynamic宣告變數
dynamic var1 = "hello";
var1 = 19;
print(var1);    // 19

// Object宣告變數
Object var2 = 20;
var2 = "Alice";
print(var2);    // Alice
複製程式碼

建議在編寫程式碼時,儘可能顯式指定變數型別,這樣可以提升程式碼可讀性與除錯的便利性。

定義常量

Dart中定義常量也有兩種方式,一種使用final關鍵字,同Java中的用法, 一個 final 變數只能賦值一次;另一種是Dart的方式,使用const關鍵字定義。

// 1.使用final關鍵字定義常量
final height = 10;

// 2.使用const關鍵字定義常量
const pi = 3.14;
複製程式碼

需要注意,final定義的常量是執行時常量,而const常量則是編譯時常量,也就是說final定義常量時,其值可以是一個變數,而const定義的常量,其值必須是一個字面常量值。

final time = new DateTime.now(); // 正確
const time = new DateTime.now(); // 錯誤


const list = const[1,2,3];       // 正確
const list = [1,2,3];            // 錯誤
複製程式碼

內建型別的常用操作

數值型別

// String 轉 int
var one = int.parse('1');

// String 轉 double
var onePointOne = double.parse('1.1');

// int 轉 String
String oneAsStr = 1.toString();

// double 轉 String
String piAsStr = 3.14159.toStringAsFixed(2); // 保留兩位 '3.14'

// Dart也支援整數位操作,<<、 >>、&、|
print((3 << 1) == 6);  // 0011 << 1 == 0110
print((3 >> 1) == 1);  // 0011 >> 1 == 0001
print((3 | 4)  == 7);  // 0011 | 0100 == 0111
複製程式碼

字串

值得一提的是,Dart中提供的字串插值表示式使字串格式化變得異常方便。

// 1.Dart可以使用單引號或雙引號來建立字串
var s1 = "hello";
var s2 = 'world';

// 2.類似Python,Dart可以使用三引號來建立包含多行的字串
var multiLine1 = """你可以像這樣,建立一個
包含了多行的字串內容
""";

var multiLine2 = '''你也可以使用三個單引號,建立一個
包含了多行的字串內容
''';

// 3.類似Python,還可以在字串字面值的前面加上`r`來建立原始字串,則該字串中特殊字元可以不用轉義
var path = r'D:\workspace\code';

// 4.Dart支援使用"+"操作符拼接字串
var greet = "hello" + " world";

// 5.Dart提供了插值表示式"${}",也可以用於拼接字串
var name = "王五";
var aStr = "hello,${name}";
print(aStr);    // hello,王五

// 當僅取變數值時,可以省略花括號
var aStr2 = "hello,$name"; // hello,王五

// 當拼接的是一個表示式時,則不能省略花括號
var str1 = "link";
var str2 = "click ${str1.toUpperCase()}";
print(str2);   // click LINK

// 6. 與Java不同,Dart使用"=="來比較字串的內容
print("hello" == "world");
複製程式碼

布林型別

Dart中的布林型別用法同Java,僅有falsetrue兩個值,不能使用0、非0或者null、非null來表達falsetrue。與Java不同的是,布林型別的預設值為null

bool flags;
print(flags);    // null
複製程式碼

列表

Dart中列表操作與JavaScript中的陣列相似。

// 建立列表
var list = [1, 2, 3];
// 下標從0開始。使用length可以訪問list的長度
print(list[0]);
print(list.length);

// 可以使用add新增元素
list.add(5);

// 可在list字面量前新增const關鍵字,定義一個不可改變的 列表(編譯時常量)
var constantList = const [1, 2, 3];
constantList[1] = 1;     // 報錯
複製程式碼

對映

又稱為關聯陣列,相當於Java中的HashMap

// 1.通過字面量建立Map
var gifts = {
  'first' : 'partridge',
  'second': 'turtledoves',
  'fifth' : 'golden rings'
};

// 2.使用Map類的建構函式建立物件
var pic = new Map();
// 往Map中新增鍵值對
pic['first'] = 'partridge';
pic['second'] = 'turtledoves';
pic['fifth'] = 'golden rings';

// 3.獲取Map的長度
print(pic.length);

// 4.查詢Map
pirnt(pic["first"]);
print(pic["four"]);    // 鍵不存在則返回 null
複製程式碼

函式

在Dart中,函式(或方法) 也是物件,它的型別是 Function。 這意味著,函式可以賦值給變數,也可以當做其他函式的引數。

定義函式

Dart中定義函式,基本上與Java類似

String greet(String name){
    return "hello,$name";
}
複製程式碼

在Dart中,型別是可選,可以省略顯式的型別,但仍然建議顯式指定型別。

greet(name){
    return "hello,$name";
}
複製程式碼

要注意,函式也是物件,所有函式都有返回值。當沒有指定返回值的時候,函式會返回null。當然,如果你強行使用void來修飾函式,則函式真的沒有返回值,這種情況就另當別論了。

函式的引數

Dart中支援兩種可選引數

  • 命名可選引數
  • 位置可選引數

在Java中通常使用方法過載來實現同名方法的不同引數呼叫,Dart中則可以通過可選引數來實現相同效果。

命名可選引數

先來看一下命名引數,它使用花括號來定義引數列表

// 定義一個函式,引數列表用花括號包裹
enableFlags({bool bold, bool hidden}) {
    // do something
}

// 呼叫方式,傳參時使用"引數名:值"的形式
enableFlags(hidden:true,bold:false);
複製程式碼

如果在定義函式時,給引數列表中的引數設定預設值,則該引數就是可選的,函式呼叫時可以忽略該引數,使用預設的值。

// 定義add函式
add({int x, int y=1, int z=0}){
    print(x + y + z;
}

// 呼叫
add(x:18);              // 19
add(x:18, y:2, z:10);   // 30
複製程式碼

這裡需要注意一下,SDK 1.21之前的版本中,命名引數不能使用=號來設定預設值,而SDK 1.21之後,只能使用=號來設定預設值。因此,請檢查並升級SDK版本。

位置可選引數

位置可選引數使用中括號來定義引數列表,中括號中的引數是可選的

// 定義add函式
add(int x, [int y, int z]){
    int result = x;
    if (y !=  null){
        result = result + y;
    }

    if (z !=  null){
        result = result + z;
    }
    print(result);
}

// 呼叫
add(18);           // 18
add(18,12);        // 30
add(18, 12, 15);   // 45
複製程式碼

位置可選引數設定預設值

// 定義add函式
add(int x, [int y=0, int z=0]){
    print(x +y+z);
}
複製程式碼

最後需要注意一下命名可選引數位置可選引數的區別,前者中的引數與順序無關,無需按順序傳參,且傳引數時需使用冒號;後者與順序相關,傳參必須依照順序。

匿名函式

大部分函式都有名字,但我們也可以建立沒有名字的函式,稱為匿名函式,也被稱為lambda表示式或者閉包。

// 定義匿名函式,並將其賦值給一個變數func,注意,函式體最後的花括號處必須有分號結束。
var func = (x,y){
    return x + y;
};

print(func(10,11));    // 21
複製程式碼

注意,匿名函式與普通函式基本相同,也有引數列表,函式體,只是省去了函式名而已。

箭頭函式

Dart中的箭頭函式與JavaScript中的基本相同。當函式體中只包含一個語句時,我們就可以使用=>箭頭語法進行縮寫。注意,箭頭函式僅僅只是一個簡潔表達的語法糖。

普通函式

add(num x, num y){
    return x + y;
}

print(add(18,12));    // 30
複製程式碼

箭頭函式

// 與上面的普通函式完全等價
add(num x, num y) => x + y;

print(add(18,12));    // 30
複製程式碼

箭頭函式省略了花括號的表達,箭頭後面跟一個表示式,函式的返回值也就是這個表示式的值。另外,箭頭函式也可以與匿名函式結合,形成匿名箭頭函式。

var func = (num x, num y) => x + y;
複製程式碼

運算子

Dart語言中的運算子與Java中的絕大多數相同。

算術運算子

+-*/%同Java語言

Dart中又多出了一個整除運算子~/,與普通除號的區別是將相除後的結果取整返回。

型別判定運算子

以下是Dart增加的型別相關的運算子。

操作符 解釋
as 用於型別轉換
is 如果物件是指定的型別就返回 True
is! 如果物件不是指定的型別返回 True

obj 實現了 T 的介面時, obj is T 才是 true。類似於Java中的instanceof

Dart中使用 as 操作符把物件轉換為特定的型別,如無法轉換則會丟擲異常,因此在轉換前最好使用is運算子進行檢測。

// 將p轉換為Person型別再操作
(p as Person).name = 'Bruce';
複製程式碼

條件表示式

Dart中也支援三目表示式 condition ? expr1 : expr2

除此外,Dart還增加了非空條件判斷符?? expr1 ?? expr2 上述運算表示,如果expr1的值不等於null,則返回其值; 否則執行表示式expr2並返回其結果。

var str1 =  "Hello";
var str2 =  "world";
var result = str1 ?? str2.toUpperCase();
複製程式碼

級聯運算子

我們通常使用.操作符呼叫物件的方法,這在Dart中也是支援的,但是Dart另外增加了一種級聯運算子..,用兩個點表示。

級聯運算子可以在同一個物件上連續呼叫多個方法以及訪問成員變數。 使用它可以避免建立臨時變數, 寫出更流暢的程式碼。

假如類Person有三個方法,setNamesetAgesave,則可如下呼叫

new Person()..setName("Bob")..setAge(20)..save();
複製程式碼

使用級聯運算子呼叫方法,無需該方法返回物件本身即可連續的流式的呼叫該物件的其他方法。

條件成員訪問符

在Java中很容易碰到惱人的空指標錯誤,因此在方法呼叫前需要進行物件的非空判斷,這樣的判斷語句使程式碼變得冗長,可讀性差,不整潔。Dart中則發明了一個新的運算子用於處理此類情況。

條件成員訪問符?.,它和.類似,但是運算子左邊的物件不能為null,否則返回null,若物件不為null,則返回物件本身。

// list1預設值為null
List list1;
print(list1?.length);  // null

List list2 = [];
print(list2?.length);  // 0
複製程式碼

分支與迴圈

條件分支

Dart中的條件分支基本與Java相同

if條件分支

if(i < 0){
  print('i < 0');
}else if(i == 0){
  print('i = 0');
} else {
  print('i > 0');
}
複製程式碼

switch條件分支

// 在switch的case中可以使用整數、字串、列舉型別和編譯時常量
String command = 'OPEN';
switch (command) {
  case 'CLOSED':
    break;
  case 'OPEN':
    break;
  default:
    print('Default');
}
複製程式碼

迴圈語句

基本迴圈

Dart中的基本迴圈語句與Java相同

// for迴圈
for(int i = 0; i < 9; i++) {
  print(i);
}

// while迴圈
while(true){
  //do something
}

// do-while迴圈
do{
  //do something
} while(true);
複製程式碼

特有迴圈

var myList = ['Java','JavaScript','Dart'];

// for...in...迴圈,類似Java中的增強for
for (var it in myList ){
    print(it);
}

// forEach迴圈。其引數為一個Function物件,這裡傳入一個匿名函式
myList.forEach((var it){
    print(it);
});

// 可以使用匿名箭頭函式簡寫
myList.forEach((it) => print(it));
複製程式碼

使用迴圈遍歷Map

var myMap = {
'zhangsan':'201901',
'lisi':'201902',
'wangwu':'201902'
};

// forEach遍歷Map
myMap.forEach((k, v) =>  print("$k : $v"));

// 根據鍵獲取值來遍歷。通過keys返回Map中所有鍵的集合
for(var k in myMap.keys){
    print("$k : ${myMap[k]}");
}
複製程式碼

我的個人部落格

GitHub

Dart語言——45分鐘快速入門

關注我的公眾號:程式設計之路從0到1

程式設計之路從0到1

相關文章