簡(jiǎn)介
Dart 在靜態(tài)語(yǔ)法方面和 Java 非常相似,如類型定義、函數(shù)聲明、泛型等,而在動(dòng)態(tài)特性方面又和 JavaScript 很像,如函數(shù)式特性、異步支持等。除了融合 Java 和 JavaScript 語(yǔ)言之所長(zhǎng)之外,Dart 也具有一些其他很有表現(xiàn)力的語(yǔ)法,如可選命名參數(shù)、..(級(jí)聯(lián)運(yùn)算符)和?.(條件成員訪問(wèn)運(yùn)算符)以及??(判空賦值運(yùn)算符)。
一、變量聲明
1.var 關(guān)鍵字
類似于 JavaScript 中的var,它可以接收任何類型的變量,但最大的不同是 Dart 中 var變量一旦賦值,類型便會(huì)確定,則不能再改變其類型,如:
var t = "hi world";
// 下面代碼在dart中會(huì)報(bào)錯(cuò),因?yàn)樽兞縯的類型已經(jīng)確定為String,
// 類型一旦確定后則不能再更改其類型。
t = 1000;
上面的代碼在 JavaScript 是沒(méi)有問(wèn)題的,前端開(kāi)發(fā)者需要注意一下,之所以有此差異是因?yàn)?Dart 本身是一個(gè)強(qiáng)類型語(yǔ)言,任何變量都是有確定類型的,在 Dart 中,當(dāng)用var聲明一個(gè)變量后,Dart 在編譯時(shí)會(huì)根據(jù)第一次賦值數(shù)據(jù)的類型來(lái)推斷其類型,編譯結(jié)束后其類型就已經(jīng)被確定,而 JavaScript 是純粹的弱類型腳本語(yǔ)言,var只是變量的聲明方式而已。
2.dynamic 和 Object
Object 是 Dart 所有對(duì)象的根基類,也就是說(shuō)在 Dart 中所有類型都是Object的子類(包括Function和Null),所以任何類型的數(shù)據(jù)都可以賦值給Object聲明的對(duì)象。 dynamic與Object聲明的變量都可以賦值任意對(duì)象,且后期可以改變賦值的類型,這和var是不同的,如:
dynamic t;
Object x;
t = "hi world";
x = 'Hello Object';
//下面代碼沒(méi)有問(wèn)題
t = 1000;
x = 1000;
dynamic與Object不同的是dynamic聲明的對(duì)象編譯器會(huì)提供所有可能的組合,而Object聲明的對(duì)象只能使用Object的屬性與方法, 否則編譯器會(huì)報(bào)錯(cuò),如:
dynamic a;
Object b = "";
main() {
a = "";
printLengths();
}
printLengths() {
// 正常
print(a.length);
// 報(bào)錯(cuò) The getter 'length' is not defined for the class 'Object'
print(b.length);
}
dynamic的這個(gè)特點(diǎn)使得我們?cè)谑褂盟鼤r(shí)需要格外注意,這很容易引入一個(gè)運(yùn)行時(shí)錯(cuò)誤,比如下面代碼在編譯時(shí)不會(huì)報(bào)錯(cuò),而在運(yùn)行時(shí)會(huì)報(bào)錯(cuò):
print(a.xx); // a是字符串,沒(méi)有"xx"屬性,編譯時(shí)不會(huì)報(bào)錯(cuò),運(yùn)行時(shí)會(huì)報(bào)錯(cuò)
3.final和const
如果從未打算更改一個(gè)變量,那么使用final或const,不是var,也不是一個(gè)類型。 一個(gè)final變量只能被設(shè)置一次,兩者區(qū)別在于:const 變量是一個(gè)編譯時(shí)常量(編譯時(shí)直接替換為常量值),final變量在第一次使用時(shí)被初始化。被final或者const修飾的變量,變量類型可以省略,如:
//可以省略String這個(gè)類型聲明
final str = "hi world";
//final String str = "hi world";
const str1 = "hi world";
//const String str1 = "hi world";
4.空安全(null-safety)
Dart 中一切都是對(duì)象,這意味著如果我們定義一個(gè)數(shù)字,在初始化它之前如果我們使用了它,假如沒(méi)有某種檢查機(jī)制,則不會(huì)報(bào)錯(cuò),比如:
test() {
int i;
print(i*8);
}
在 Dart 引入空安全之前,上面代碼在執(zhí)行前不會(huì)報(bào)錯(cuò),但會(huì)觸發(fā)一個(gè)運(yùn)行時(shí)錯(cuò)誤,原因是 i 的值為 null 。但現(xiàn)在有了空安全,則定義變量時(shí)我們可以指定變量是可空還是不可空。
int i = 8; //默認(rèn)為不可空,必須在定義時(shí)初始化。
int? j; // 定義為可空類型,對(duì)于可空變量,我們?cè)谑褂们氨仨毰锌铡?
// 如果我們預(yù)期變量不能為空,但在定義時(shí)不能確定其初始值,則可以加上late關(guān)鍵字,
// 表示會(huì)稍后初始化,但是在正式使用它之前必須得保證初始化過(guò)了,否則會(huì)報(bào)錯(cuò)
late int k;
k=9;
如果一個(gè)變量我們定義為可空類型,在某些情況下即使我們給它賦值過(guò)了,但是預(yù)處理器仍然有可能識(shí)別不出,這時(shí)我們就要顯式(通過(guò)在變量后面加一個(gè)”!“符號(hào))告訴預(yù)處理器它已經(jīng)不是null了,比如:
class Test{
int? i;
Function? fun;
say(){
if(i!=null) {
print(i! * 8); //因?yàn)橐呀?jīng)判過(guò)空,所以能走到這 i 必不為null,如果沒(méi)有顯式申明,則 IDE 會(huì)報(bào)錯(cuò)
}
if(fun!=null){
fun!(); // 同上
}
}
}
上面中如果函數(shù)變量可空時(shí),調(diào)用的時(shí)候可以用語(yǔ)法糖:
fun?.call() // fun 不為空時(shí)則會(huì)被調(diào)用
二、函數(shù)
Dart是一種面向?qū)ο蟮恼Z(yǔ)言,所以即使是函數(shù)也是對(duì)象,并且有一個(gè)類型Function。這意味著函數(shù)可以賦值給變量或作為參數(shù)傳遞給其他函數(shù),這是函數(shù)式編程的典型特征。
1.函數(shù)聲明
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
Dart函數(shù)聲明如果沒(méi)有顯式聲明返回值類型時(shí)會(huì)默認(rèn)當(dāng)做dynamic處理,注意,函數(shù)返回值沒(méi)有類型推斷
typedef bool CALLBACK();
//不指定返回類型,此時(shí)默認(rèn)為dynamic,不是bool
isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
void test(CALLBACK cb){
print(cb());
}
//報(bào)錯(cuò),isNoble不是bool類型
test(isNoble);
對(duì)于只包含一個(gè)表達(dá)式的函數(shù),可以使用簡(jiǎn)寫(xiě)語(yǔ)法:
bool isNoble (int atomicNumber)=> true ;
2.函數(shù)作為變量
var say = (str){
print(str);
};
say("hi world");
3.函數(shù)作為參數(shù)傳遞
//定義函數(shù)execute,它的參數(shù)類型為函數(shù)
void execute(var callback) {
callback(); //執(zhí)行傳入的函數(shù)
}
//調(diào)用execute,將箭頭函數(shù)作為參數(shù)傳遞
execute(() => print("xxx"))
(1)可選的位置參數(shù)
包裝一組函數(shù)參數(shù),用[]標(biāo)記為可選的位置參數(shù),并放在參數(shù)列表的最后面:
String say(String from, String msg, [String? device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
下面是一個(gè)不帶可選參數(shù)調(diào)用這個(gè)函數(shù)的例子:
say('Bob', 'Howdy'); //結(jié)果是: Bob says Howdy
下面是用第三個(gè)參數(shù)調(diào)用這個(gè)函數(shù)的例子:
say('Bob', 'Howdy', 'smoke signal'); //結(jié)果是:Bob says Howdy with a smoke signal
(2)可選的命名參數(shù)
定義函數(shù)時(shí),使用{param1, param2, …},放在參數(shù)列表的最后面,用于指定命名參數(shù)。例如:
//設(shè)置[bold]和[hidden]標(biāo)志
void enableFlags({bool bold, bool hidden}) {
// ...
}
調(diào)用函數(shù)時(shí),可以使用指定命名參數(shù)。例如:paramName: value
enableFlags(bold: true, hidden: false);
可選命名參數(shù)在Flutter中使用非常多。**注意,不能同時(shí)使用可選的位置參數(shù)和可選的命名參數(shù)。
三、mixin
Dart 是不支持多繼承的,但是它支持 mixin,簡(jiǎn)單來(lái)講 mixin 可以 “組合” 多個(gè)類,我們通過(guò)一個(gè)例子來(lái)理解。
定義一個(gè) Person 類,實(shí)現(xiàn)吃飯、說(shuō)話、走路和寫(xiě)代碼功能,同時(shí)定義一個(gè) Dog 類,實(shí)現(xiàn)吃飯、和走路功能:
class Person {
say() {
print('say');
}
}
mixin Eat {
eat() {
print('eat');
}
}
mixin Walk {
walk() {
print('walk');
}
}
mixin Code {
code() {
print('key');
}
}
class Dog with Eat, Walk{}
class Man extends Person with Eat, Walk, Code{}