基礎(chǔ)類型
Number類型
Number中包含了兩個(gè)類型int和double。分別代表整數(shù)類型和雙精度浮點(diǎn)類型。
- int
int類型在不同的環(huán)境中長(zhǎng)度并不一樣,在DartVM上,int的范圍是-2^63 至 2^63 - 1,而在web中,int的范圍是-2^53 至 2^53 - 1
定義int型變量可以通過(guò)以下幾種方式,其中通過(guò)var來(lái)定義變量,可以不需要指定類型,編譯器可根據(jù)字面量進(jìn)行推斷。
int a = 1;
int b = 0xDEFF0;
var c = 2;
注意:通過(guò)
int類型來(lái)定義變量時(shí),如果定義的是全局變量一定要賦值(或聲明late),否則編譯器會(huì)報(bào)錯(cuò)。如果是定義局部變量,則在使用前必須賦值,否則使用時(shí)會(huì)拋出異常。這是因?yàn)樵赿art的空安全機(jī)制的限制。
- double
double64位雙精度浮點(diǎn)數(shù)字。
定義double變量,可通過(guò)以下方式,如果顯示的申明變量類型是double,字面量為整形時(shí),必要時(shí)會(huì)自動(dòng)轉(zhuǎn)換成double型
var a = 1.0;
double b = 2.0;
double c = 1;
其他
Number類型的變量都支持基本的運(yùn)算符+ - * /等,還有ceil() abs() floor()等方法。整型支持傳統(tǒng)的位移操作,比如移位(<<、>>和 >>>)、補(bǔ)碼 (~)、按位與 (&)、按位或 (|) 以及按位異或 (^)。
print((3 << 1) == 6); // true; 0011 << 1 == 0110
print((3 | 4) == 7); // true; 0011 | 0100 == 0111
print((3 & 4) == 0); // true; 0011 & 0100 == 0000
String
Dart中的字符串包含了 UTF-16 編碼的字符序列??梢允褂脝我?hào)或者雙引號(hào)來(lái)創(chuàng)建字符串
var str1 = '我是一個(gè)字符串';
var str2 = "我也是一個(gè)字符串"
'與"的區(qū)別:在'中需要使用\來(lái)轉(zhuǎn)義那些與單引號(hào)沖突的字符:
var str1 = 'it\'s easy to escape the string delimiter';
模版字符串
在字符串中,請(qǐng)以 ${表達(dá)式} 的形式使用表達(dá)式,如果表達(dá)式是一個(gè)標(biāo)識(shí)符,可以省略掉 {}。如果表達(dá)式的結(jié)果為一個(gè)對(duì)象,則 Dart 會(huì)調(diào)用該對(duì)象的 toString 方法來(lái)獲取一個(gè)字符串。
var s = 'string interpolation';
print('Dart has $s, which is very handy.' ==
'Dart has string interpolation, '
'which is very handy.'); // true
print('That deserves all caps. '
'${s.toUpperCase()} is very handy!' ==
'That deserves all caps. '
'STRING INTERPOLATION is very handy!'); // true
字符串字面量是一個(gè)編譯時(shí)常量,只要是編譯時(shí)常量 (null、數(shù)字、字符串、布爾) 都可以作為字符串字面量的插值表達(dá)式:
// 這里定義為常量是可行的
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';
// 非常量的字符串是不被允許使用在定義字符串的模版內(nèi)
var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = [1, 2, 3];
const validConstString = '$aConstNum $aConstBool $aConstString';
// const invalidConstString = '$aNum $aBool $aString $aConstList';
字符串拼接
我們可以使用+號(hào)或者并列放置在多行的字符串來(lái)拼接字符串。
var str1 = 'the + operate' + ' works';
var str2 = 'String '
'concatenation'
" works even over line breaks.";
多行字符串
使用三個(gè)' 或者三個(gè)"來(lái)定義多行字符串
var s1 = '''
You can create
multi-line strings like this one.
''';
var s2 = """This is also a
multi-line string.""";
raw字符串
通過(guò)在字符串前加上r做前綴可創(chuàng)建raw字符串,即字符串不會(huì)被做任何處理(如轉(zhuǎn)義)。
var s = r'In a raw string, not even \n gets special treatment.';
print(s); // In a raw string, not even \n gets special treatment.
String與Number類型轉(zhuǎn)換
下面是字符串與數(shù)字類型的轉(zhuǎn)換方式:
// String -> int
var one = int.parse('1');
print(one == 1); // true
// String -> double
var onePointOne = double.parse('1.1');
print(onePointOne == 1.1); // true
// int -> String
String oneAsString = 1.toString();
print(oneAsString == '1'); //true
// double -> String
String piAsString = 3.14159.toStringAsFixed(2);
print(piAsString == '3.14'); //true
布爾型
Dart 使用 bool 關(guān)鍵字表示布爾類型,布爾類型只有兩個(gè)對(duì)象 true 和 false,兩者都是編譯時(shí)常量
Dart的類型安全檢查不允許出現(xiàn)一個(gè)非bool型的判斷,例如if(0) assert(1);
List
Dart使用 bool 關(guān)鍵字表示數(shù)組類型,字面量是由逗號(hào)分隔的一串表達(dá)式或值并以方括號(hào) [] 包裹而組成的:
var lList = [1,2,3]; // 編譯器會(huì)自動(dòng)推斷出數(shù)組的類型List<int>,如果向數(shù)組里添加一個(gè)非int類型,會(huì)報(bào)錯(cuò)
在字面量前面加上關(guān)鍵字 const,會(huì)定義一個(gè)編譯時(shí)常量,那么就不能執(zhí)行賦值操作,如:
var list = const [1,2,3];
list[1] = 0; // 報(bào)錯(cuò)??!
索引
跟其他語(yǔ)言一樣,在Dart里,List的索引也是從0開(kāi)始的,最后一個(gè)元素的索引位置位length-1,可以通過(guò)[]來(lái)獲取對(duì)應(yīng)索引的值。
var list = [1, 2, 3];
print(list.length); // 3
print(list[1]); // 2
list[1] = 1;
print(list[1]); // 1
擴(kuò)展符
Dart提供了 ... 和 ...?的擴(kuò)展操作,可以將數(shù)組中所有元素插入另一個(gè)數(shù)組內(nèi), 而 ...? 空判斷擴(kuò)展可以避免將null插入到目標(biāo)數(shù)組。
var list = [1, 2, 3];
var list2 = [0, ...list];
print(list2.length); // 4
print(list2); // [0,1,2,3]
List? list3 = null;
var list4 = [0, ...?list3];
print(list4); // 1
控制語(yǔ)句
Dart 還同時(shí)引入了 集合中的 if 和 集合中的 for 操作,在構(gòu)建集合時(shí),可以使用條件判斷 if和循環(huán) for。
var promoActive = false;
var nav = ['Home', 'Furniture', 'Plants', if (promoActive) 'Outlet'];
print(nav); // ['Home', 'Furniture', 'Plants'];
var listOfInts = [1, 2, 3];
var listOfStrings = ['#0', for (var i in listOfInts) '#$i'];
print(listOfStrings[1]); // #1
集合
Dart中使用Set來(lái)表示集合類型,它是一組特定集合的無(wú)序集合??梢酝ㄟ^(guò)字面量{}和Set類來(lái)定義
var set = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
Dart 會(huì)根據(jù)字面量來(lái)推斷變量是一個(gè)Set<String>類型,如果往set對(duì)象中添加其他類型的元素,會(huì)拋異常
可以通過(guò)在{}前加上類型來(lái)定義一個(gè)空的集合,或者通過(guò)將{}賦值給一個(gè)Set類型的變量來(lái)實(shí)現(xiàn)。
var set = <String>{};
Set set2 = {};
Map的字面量語(yǔ)法和Set的字面量語(yǔ)法很接近,因?yàn)楝F(xiàn)有Map的字面量語(yǔ)法,因此{(lán)}定義的是一個(gè)Map而不是Set,Dart會(huì)為{}創(chuàng)建一個(gè)Map<dynamic, dynamic>對(duì)象。
常用用法
- 使用
add或者addAll方法向集合中添加元素 -
const定義的常量集合,不能調(diào)用add等方法,否則會(huì)報(bào)錯(cuò) -
Set支持...擴(kuò)展,支持if/for控制語(yǔ)句 - 可以通過(guò)
length來(lái)獲取集合的數(shù)量
Map
Map 是用來(lái)關(guān)聯(lián) keys 和 values 的對(duì)象。其中鍵和值都可以是任何類型的對(duì)象。每個(gè)鍵只能出現(xiàn)一次但是值可以重復(fù)出現(xiàn)多次。 Dart中 Map 提供了 Map 字面量以及 Map 類型兩種形式的 Map:
- 通過(guò)字面量定義Map
var gifts = {
// Key: Value
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings'
};
var nobleGases = {
2: 'helium',
10: 'neon',
18: 'argon',
};
Dart 將
gifts推斷為Map<String, String>類型,而將nobleGases推斷位Map<int, String>類型,如果向其中添加不正確的類型,則會(huì)拋出異常。
- 通過(guò)Map類型來(lái)定義Map
var gifts = Map<String, String>();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
var nobleGases = Map<int, String>();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';
Map的常用用法
- 給Map添加一組鍵值對(duì),可通過(guò)
gifts['third']=glasses; - 獲取Map中的值可以使用
gifts['forth']; - 判斷某個(gè)key是否存在使用
gifts['sixth']==null; - 使用
const定義常量的Map時(shí),不可以添加,修改操作 - Map同樣支持
...擴(kuò)展操作和if/for控制語(yǔ)句
函數(shù)
Dart是一種真正面向?qū)ο蟮恼Z(yǔ)言,所以即便函數(shù)也是對(duì)象并且類型為 Function,這意味著函數(shù)可以被賦值給變量或者作為其它函數(shù)的參數(shù)。
我們可以像這樣定義一個(gè)函數(shù) :
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
也可以省略 返回類型和參數(shù)類型(不建議這樣做)
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
如果函數(shù)內(nèi)只包含一個(gè)表達(dá)式,那么也可以簡(jiǎn)寫(xiě)成:
bool isNoble(int atomicNumber)=> _nobleGases[atomicNumber] != null;
語(yǔ)法
=>表達(dá)式是{ return 表達(dá)式; }的簡(jiǎn)寫(xiě),=>有時(shí)也稱之為箭頭函數(shù);=>后面只能跟表達(dá)式,不能跟if判斷語(yǔ)句
參數(shù)
函數(shù)的參數(shù)主要分兩種: 必要參數(shù)和可選參數(shù)。必要參數(shù)必須定義在參數(shù)列表前面,可選參數(shù)定義在必要參數(shù)的后面??蛇x參數(shù)可以是「命名的」也可以是「位置的」。
命名參數(shù)
命名參數(shù)默認(rèn)時(shí)可選的,除非使用了required關(guān)鍵字標(biāo)記。
命名參數(shù)使用 {param1, param2}來(lái)定義,如果沒(méi)有指定默認(rèn)值,沒(méi)有required標(biāo)記,默認(rèn)值為null
void enableFlags({bool? bold, bool? hidden}){
...
}
//使用
enableFlags(bold:true, hidden:false);
可以使用 = 來(lái)給參數(shù)指定默認(rèn)值,指定的默認(rèn)值必須時(shí)一個(gè)常量
void enableFlags({bool bold = false, bool hidden = false}) {...}
// hidden參數(shù)位false
enableFlags(bold: true);
如果希望某個(gè)參數(shù)時(shí)必須要傳的,那么就可以使用required來(lái)修飾,例如:
const Scrollbar({super.key, required Widget child});
如果創(chuàng)建一個(gè)Scrollbar不包含child的時(shí),編譯器就會(huì)報(bào)錯(cuò)
required 參數(shù)也是可以為null的。
位置參數(shù)
使用 [] 將一系列參數(shù)包裹起來(lái),即可將其標(biāo)記為位置參數(shù),因?yàn)樗鼈兊哪J(rèn)值是 null,所以如果沒(méi)有提供默認(rèn)值的話,它們的類型必須得是允許為空 nullable 的類型
String say(String from, String msg, [String? device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
//不使用位置參數(shù)調(diào)用函數(shù)
print(say('Bob', 'Howdy')); // Bob says Howdy
//使用位置參數(shù)調(diào)用函數(shù)
print(say('Bob', 'Howdy', 'smoke signal')); //Bob says Howdy with a smoke signal
可以使用 = 來(lái)為一個(gè)位置可選參數(shù)指定除了 null 以外的默認(rèn)值。指定的默認(rèn)值必須要為編譯時(shí)的常量
String say(String from, String msg, [String device = 'carrier pigeon']) {
var result = '$from says $msg with a $device';
return result;
}
print(say('Bob', 'Howdy')); //Bob says Howdy with a carrier pigeon
mian()
main() 頂級(jí)函數(shù)作為程序的入口, main() 函數(shù)返回值為 void 并且有一個(gè) List<String> 類型的可選參數(shù)。
函數(shù)是一級(jí)對(duì)象
可以將函數(shù)作為參數(shù)傳遞給另一個(gè)函數(shù)。例如:
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
// 將printElement作為一個(gè)參數(shù)
list.forEach(printElement); // 1 2 3
也可以將函數(shù)賦值給一個(gè)變量,比如:
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
print(loudify('hello')); // !!! HELLO !!!
匿名函數(shù)
顧名思義,就是沒(méi)有名字; 通常稱為 匿名函數(shù)、 Lambda 表達(dá)式 或 Closure 閉包;可以將匿名函數(shù)賦值給一個(gè)變量然后使用。
匿名函數(shù)的定義如下
(param1, params2) {
...
}
下面例子就是定義了一個(gè)只有一個(gè)參數(shù)item的匿名函數(shù)用來(lái)打印List中每個(gè)字符串和字符串長(zhǎng)度:
const list = ['apples', 'bananas', 'oranges'];
list.map((item) {
return item.toUpperCase();
}).forEach((item) {
print('$item: ${item.length}'); // APPLES: 6 BANANAS: 7 ORANGES: 7
});
運(yùn)算符
下表中是常用的運(yùn)算符,表中從上到下也表示運(yùn)算符的優(yōu)先級(jí)順序。
| 描述 | 操作符 |
|---|---|
| 一元后綴 | expr++ expr-- () [] ?[] . ?. ! |
| 一元前綴 | -expr !expr ~expr ++expr --expr await expr |
| 乘除運(yùn)算 | * / % ~/ |
| 加減運(yùn)算 | + - |
| 位移 | << >> >>> |
| 位運(yùn)算AND | & |
| 位運(yùn)算異或 | ^ |
| 位運(yùn)算或 | | |
| 關(guān)系類型判斷 | >= > <= < as is is! |
| 相等 | == != |
| 邏輯與 | && |
| 邏輯或 | |
| 如果空 | ?? |
| 三元運(yùn)算 | expr1 ? expr2 : expr3 |
| 級(jí)聯(lián) | .. ?.. |
| 復(fù)合運(yùn)算 | = *= /= += -= &= ^= etc. |
類型判斷
|操作符|含義|
|--|--|
|as|類型轉(zhuǎn)換|
|is|如果對(duì)象是指定對(duì)象返回true|
|is!|如果對(duì)象是指定對(duì)象返回false|
當(dāng) obj 實(shí)現(xiàn)了 T 接口時(shí), obj is T 才會(huì)返回 true;
只有當(dāng)obj明確是某個(gè)對(duì)象類型時(shí),才使用as
// 1
(employee as Person).firstName = 'Bob';
// 2
if (employee is Person) {
// 類型檢查
employee.firstName = 'Bob';
}
在上面例子中,如果
employee是null或者不是Person類型時(shí), 注釋1處語(yǔ)句就會(huì)拋出異常,而注釋2處不會(huì)拋出異常。
賦值運(yùn)算
??= 使用場(chǎng)景 obj ??= 123 ,當(dāng)obj為 null時(shí),才會(huì)賦值 123否則不處理。
條件表達(dá)式
表達(dá)式1 ?? 表達(dá)式2
如果表達(dá)式 1 為
非 null則返回其值,否則執(zhí)行表達(dá)式 2 并返回其值
級(jí)聯(lián)表達(dá)式
級(jí)聯(lián)運(yùn)算符 .., ?.. 可以在同一個(gè)對(duì)象上連續(xù)調(diào)用多個(gè)對(duì)象的變量或方法
var paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0;
等同于代碼:
var paint = Paint();
paint.color = Colors.black;
paint.strokeCap = StrokeCap.round;
paint.strokeWidth = 5.0;
如果對(duì)象可能為null的話,可以使用?..
querySelector('#confirm')
?..text = 'Confirm'
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'))
..scrollIntoView();
代碼等同于
var button = querySelector('#confirm');
button?.text = 'Confirm';
button?.classes.add('important');
button?.onClick.listen((e) => window.alert('Confirmed!'));
button?.scrollIntoView();
級(jí)聯(lián)對(duì)象可以嵌套使用:
final addressBook = (AddressBookBuilder()
..name = 'jenny'
..email = 'jenny@example.com'
..phone = (PhoneNumberBuilder()
..number = '415-555-0100'
..label = 'home')
.build())
.build();
在調(diào)用返回對(duì)象的函數(shù)時(shí),一定要謹(jǐn)慎使用級(jí)聯(lián)操作;如果函數(shù)返回
void,那么就不能使用級(jí)聯(lián)操作。
其他運(yùn)算符
| 操作符 | 含義 |
|---|---|
| ?[] | 判空訪問(wèn)List,不為空時(shí)訪問(wèn)List中元素 |
| ?. | 條件訪問(wèn)成員,不為空時(shí)訪問(wèn)成員 |
| ! | 斷言操作,認(rèn)為一定不為空訪問(wèn)相關(guān)成員,如果為空拋出異常 |
控制語(yǔ)句
if-else
與其他語(yǔ)言類似,通過(guò)條件判斷執(zhí)行代碼塊 其中 else 和 else if 是可選判斷。但是判斷的條件一定是bool型,不可以是其他類型。
if(表達(dá)式1){
...
}else if(表達(dá)式2){
...
}else{
...
}
for循環(huán)
- 可以使用標(biāo)準(zhǔn)的for循環(huán)進(jìn)行迭代
for(int i=0; i<10; i++) - 使用for-in方法遍歷一個(gè)可迭代的對(duì)象
for(final a in objs) - 使用可迭代對(duì)象的forEach方法
iteractor.forEach()
while 和 do-while循環(huán)
while 和 do-while的區(qū)別是:
-
while會(huì)先進(jìn)行判斷,然后再執(zhí)行代碼塊, -
do-while是先執(zhí)行代碼塊,再進(jìn)行判斷
contin 和 break
continue 表示跳過(guò)此次循環(huán),break表示跳出循環(huán)
如果在Iterable對(duì)象中,可以使用where代替continue:
candidates
.where((c) => c.yearsExperience >= 5)
.forEach((c) => c.interview());
switch-case
Switch 語(yǔ)句在Dart中使用 == 來(lái)比較整數(shù)、字符串或編譯時(shí)常量,比較的兩個(gè)對(duì)象必須是同一個(gè)類型且不能是子類并且沒(méi)有重寫(xiě) == 操作符。 枚舉類型非常適合在 Switch 語(yǔ)句中使用。
非空case想要實(shí)現(xiàn)穿透可通過(guò)continue來(lái)實(shí)現(xiàn):
var command = 'CLOSED';
switch (command) {
case 'CLOSED':
executeClosed();
continue nowClosed;
nowClosed:
case 'NOW_CLOSED':
executeNowClosed();
break;
}
每個(gè)case內(nèi)都可以有局部變量,并且只在當(dāng)前case可見(jiàn)。
注釋
單行注釋
單行注釋以//開(kāi)始,所在行的內(nèi)容都屬于注釋內(nèi)容
// 這是一個(gè)單行注釋
多行注釋
多行注釋以/*開(kāi)頭,以*/結(jié)尾,在/*和*/之間的內(nèi)容都會(huì)被編譯器忽略,認(rèn)為是注釋內(nèi)容
/*
這是一個(gè)多行注釋
注釋第二行內(nèi)容
...
*/
/*
嵌套注釋的外層
/*
嵌套注釋內(nèi)層
*/
嵌套注釋外層
*/
文檔注釋
文檔注釋使用/// 或者是/**,多個(gè)連續(xù)///注釋與多行注釋效果一致。多行文檔注釋,編譯器會(huì)忽略注釋的內(nèi)容,但是如果使用了[],則編譯器會(huì)產(chǎn)生引用括號(hào)內(nèi)的內(nèi)容如類、方法、字段、頂級(jí)變量、函數(shù)和參數(shù)。
///這是一個(gè)文檔注釋舉例
///如果使用了中括號(hào),則會(huì)解析括號(hào)內(nèi)容,引用相關(guān)內(nèi)容[feed]
上面舉例中[feed]將會(huì)生成一個(gè)鏈接,指向feed的方法文檔