最好的Dart語言學(xué)習(xí)教程是官網(wǎng)
英文原版官網(wǎng)
也可以看中文版的
以下知識點(diǎn)總結(jié)基于 Dart VM version: 2.5.0
-
1.Dart是一個(gè)函數(shù)式編程語言
在函數(shù)式編程中码党,你可以做到: 1.將函數(shù)當(dāng)做參數(shù)進(jìn)行傳遞 2.將函數(shù)直接賦值給變量 3,對函數(shù)進(jìn)行解構(gòu)疟游,只傳遞給函數(shù)一部分參數(shù)來調(diào)用它蜗搔, 讓它返回一個(gè)函數(shù)去處理剩下的參數(shù)(也被稱為柯里化) 4.創(chuàng)建一個(gè)可以被當(dāng)作為常量的匿名函數(shù) (也被稱為 lambda 表達(dá)式丧荐,在 Java 的 JDK 8 release 中支持了 lambda 表達(dá)式)
重要概念(來自官網(wǎng)的翻譯) 當(dāng)你在學(xué)習(xí) Dart 語言時(shí), 應(yīng)該牢記以下幾點(diǎn):
1.所有變量引用的都是 對象梗逮,每個(gè)對象都是一個(gè) 類 的實(shí)例辩撑。數(shù)字蛹屿、函數(shù)以及 null 都是對象方面。所有的類都繼承于 Object 類放钦。
2.盡管 Dart 是強(qiáng)類型語言,但是在聲明變量時(shí)指定類型是可選的恭金,因?yàn)?Dart 可以進(jìn)行類型推斷操禀。
在上述代碼中,變量 number 的類型被推斷為 int 類型横腿。
如果想顯式地聲明一個(gè)不確定的類型颓屑,可以使用特殊類型 dynamic斤寂。
3.Dart 支持泛型,比如 List<int>(表示一組由 int 對象組成的列表)或 List<dynamic>(表示一組由任何類型對象組成的列表)揪惦。
4.Dart 支持頂級函數(shù)(例如 main 方法)遍搞,同時(shí)還支持定義屬于類或?qū)ο蟮暮瘮?shù)(即 靜態(tài) 和 實(shí)例方法)
。你還可以在函數(shù)中定義函數(shù)(嵌套 或 局部函數(shù))器腋。
5.Dart 支持頂級 變量溪猿,以及定義屬于類或?qū)ο蟮淖兞浚o態(tài)和實(shí)例變量)。實(shí)例變量有時(shí)稱之為域或?qū)傩浴?
6.Dart 沒有類似于 Java 那樣的 public纫塌、protected 和 private 成員訪問限定符诊县。
如果一個(gè)標(biāo)識符以下劃線 (_) 開頭則表示該標(biāo)識符在庫內(nèi)是私有的』ご粒可以查閱 庫和可見性 獲取更多相關(guān)信息翎冲。
7.標(biāo)識符 可以以字母或者下劃線 (_) 開頭,其后可跟字符和數(shù)字的組合媳荒。
8.Dart 中 表達(dá)式 和 語句 是有區(qū)別的抗悍,表達(dá)式有值而語句沒有。
比如條件表達(dá)式 expression condition ? expr1 : expr2 中含有值 expr1 或 expr2钳枕。
與 if-else 分支語句相比缴渊,if-else 分支語句則沒有值。
一個(gè)語句通常包含一個(gè)或多個(gè)表達(dá)式鱼炒,但是一個(gè)表達(dá)式不能只包含一個(gè)語句衔沼。
9.Dart 工具可以顯示 警告 和 錯(cuò)誤 兩種類型的問題。警告表明代碼可能有問題但不會(huì)阻止其運(yùn)行昔瞧。
錯(cuò)誤分為編譯時(shí)錯(cuò)誤和運(yùn)行時(shí)錯(cuò)誤指蚁;編譯時(shí)錯(cuò)誤代碼無法運(yùn)行;運(yùn)行時(shí)錯(cuò)誤會(huì)在代碼運(yùn)行時(shí)導(dǎo)致異常自晰。
1.程序運(yùn)行的入口函數(shù) void main()
void main() {
print('Hello, World!');
}
2.關(guān)于變量定義和類型
dart的基本變量類型有
numbers
strings
booleans
lists (also known as arrays)
sets
maps
runes (for expressing Unicode characters in a string)
symbols
//dynamic 定義一個(gè)類型可變的變量
dynamic value = "";
//獲取變量類型
print(value.runtimeType);//String
value = 1;
print(value.runtimeType);//int
value = [];
print(value.runtimeType);//List<dynamic>
value = <int>[];
print(value.runtimeType);//List<int>
//使用 var 定義變量
//bool變量
var isDebug = true;
//定義字符串變量
//雙引號
var name = "hello world dart";
//單引號
var name2 = 'hello world dart';
//單雙混用
var name3 = 'hello world "dart"';
var name4 = "hello world 'dart'";
//三個(gè)雙引號(按書寫格式輸出)
var name5 = """
hello
world
dart
""";
//三個(gè)單引號(按書寫格式輸出)
var name6 = '''
hello
world
dart
''';
//創(chuàng)建一個(gè)原樣的字符串凝化,不會(huì)轉(zhuǎn)義,回車換行也不會(huì)生效酬荞,原樣輸出
var s = r'In a raw string,not even \n gets special treatment';
var s1 = 'test test \ntest2';
print(s);//不會(huì)換行
print(s1);//會(huì)換行
print(r"^((https|http|ftp|rtsp|mms)?:\/\/)[^\s]+");//打印結(jié)果 ^((https|http|ftp|rtsp|mms)?:\/\/)[^\s]+
print("^((https|http|ftp|rtsp|mms)?:\/\/)[^\s]+");//打印結(jié)果 ^((https|http|ftp|rtsp|mms)?://)[^s]+
//dart里只有兩個(gè)數(shù)值類型 int double 都是num的子類
//定義int變量
var year = 1977;
//定義double變量
var antennaDiameter = 3.7;
//定義List變量
var flybyObjects = ['Jupiter', 'Saturn', 'Uranus', 'Neptune'];
var emptyList = <String>[];//List<String>
//定義Set變量
var setObjects = {'Jupiter', 'Saturn', 'Uranus', 'Neptune'};
var emptySet = <String>{};//Set<String>
//定義map變量
var image = {
'tags': ['saturn'],
'url': '//path/to/saturn.jpg'
};
var emptyMap = {};//Map<String, Object>
//除了用var定義變量外
//可以直接聲明類型
double z = 1;
3.流程控制語句
if (year >= 2001) {
print('21st century');
} else if (year >= 1901) {
print('20th century');
}
for (var object in flybyObjects) {
print(object);
}
for (int month = 1; month <= 12; month++) {
print(month);
}
while (year < 2016) {
year += 1;
}
dart支持switch搓劫,并且可以使用 continue和label
If you really want fall-through, you can use a continue statement and a label:
var command = 'CLOSED';
switch (command) {
case 'CLOSED':
executeClosed();
continue nowClosed;
// 繼續(xù)執(zhí)行標(biāo)簽為 nowClosed 的 case 子句。
nowClosed:
case 'NOW_CLOSED':
// case 條件值為 CLOSED 和 NOW_CLOSED 時(shí)均會(huì)執(zhí)行該語句混巧。
executeNowClosed();
break;
}
4.定義方法
//最好都指定返回值
int fibonacci(int n) {
if (n == 0 || n == 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
var result = fibonacci(20);
//如果方法里只有一行枪向,可以簡寫(可以省略{}和return)
int add(int a,int b) {
return a+b;
}
//可以簡寫成
int add(int a,int b) => a+b;
//定義一個(gè)無參匿名方法并立即運(yùn)行
(){
print("hello world");
}();
//定義一個(gè)有參匿名方法并立即運(yùn)行
(int i,int j){
print('i+j=${i+j}');
}(7,8);
//定義一個(gè)函數(shù),賦值給一個(gè)變量
var fun = () {
print("hello world");
};
print("fun is function:${fun is Function}");//true
fun();
5.注釋
//單行注釋
var intValue = 1;
///多行注釋咧党,
///可用于dartdoc
var doubleValue = 1.0;
/*支持單行和多行注釋*/
var comments = "這是注釋";
6.導(dǎo)包
// 導(dǎo)入dart核心庫
import 'dart:math';
// Importing libraries from external packages
import 'package:test/test.dart';
// Importing files
import 'path/to/my_other_file.dart';
//show hide
// 只導(dǎo)入 lib1 中的 foo秘蛔。(Import only foo).
import 'package:lib1/lib1.dart' show foo;
// 導(dǎo)入 lib2 中除了 foo 外的所有。
import 'package:lib2/lib2.dart' hide foo;
//as
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// 使用 lib1 的 Element 類。
Element element1 = Element();
// 使用 lib2 的 Element 類缠犀。
lib2.Element element2 = lib2.Element();
//延遲加載庫
import 'package:greetings/hello.dart' deferred as hello;
Future greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
7.類 class
class Spacecraft {
// 私有變量数苫,當(dāng)前.dart文件內(nèi)可以訪問聪舒,其他dart文件類里面不能訪問這一個(gè)變量
int _speed = 0;
String name;
DateTime launchData;
//帶有語法糖的構(gòu)造函數(shù)辨液,使用創(chuàng)建對象同時(shí)并給成員變量賦值
Spacecraft(this.name,this.launchData) {
// 初始化代碼
}
//命名構(gòu)造函數(shù)
//重定向構(gòu)造方法
//有時(shí)一個(gè)構(gòu)造方法僅僅用來重定向到該類的另一個(gè)構(gòu)造方法。
//重定向方法沒有主體箱残,它在冒號(:)之后調(diào)用另一個(gè)構(gòu)造方法滔迈。
Spacecraft.unlaunched(String name) : this(name,null);
//構(gòu)造函數(shù)成員變量初始化列表,成員變量初始化列表賦值比構(gòu)造函數(shù)函數(shù)體還要早運(yùn)行的
Spacecraft.fromJson(Map<String, dynamic> json)
: name = json["name"],
launchData = DateTime.fromMicrosecondsSinceEpoch(json["launchData"]) {
print("fromJson $launchData");
}
//構(gòu)造函數(shù)上也可以加斷言被辑,調(diào)試模式下燎悍,如果斷言條件不成立,會(huì)拋異常盼理,及時(shí)發(fā)現(xiàn)錯(cuò)誤
Spacecraft.fromJson2(Map<String, dynamic> json)
: assert(json.containsKey("name")),
assert(json.containsKey("launchData")) {
print("fromJson $launchData");
}
//Dart 支持工廠構(gòu)造方法谈山。它能夠返回其子類甚至 null 對象。要?jiǎng)?chuàng)建一個(gè)工廠構(gòu)造方法宏怔,請使用 factory 關(guān)鍵字奏路。
//工廠構(gòu)造方法不支持this.參數(shù),不支持初始化列表臊诊,不支持?jǐn)嘌? factory Spacecraft.fromJson3() {
return null;
}
// 當(dāng)作常量使用的匿名函數(shù)
int get launchYear => launchData?.year;
//無參無返回值方法
void describe(){
print("Spacecraft:$name");
}
//私有的函數(shù)鸽粉,以_開頭的函數(shù),只能在類內(nèi)部使用抓艳,不能在外部使
void _privateFunction() {
}
}
//使用時(shí)
var voyager = Spacecraft('Voyager I', DateTime(1977, 9, 5));
voyager.describe();
var voyager3 = Spacecraft.unlaunched('Voyager III');
voyager3.describe();
//Const 構(gòu)造方法
//如果你的類生成的對象永遠(yuǎn)都不會(huì)更改触机,則可以讓這些對象成為編譯時(shí)常量,會(huì)放到常量池
//為此玷或,請定義 const 構(gòu)造方法并確保所有實(shí)例變量都是 final 的。
class ImmutablePoint {
const ImmutablePoint(this.x, this.y);
final int x;
final int y;
static const ImmutablePoint origin = ImmutablePoint(0, 0);
}
var a = ImmutablePoint.origin;//常量
var a1 = const ImmutablePoint(0, 0);//常量
var a2 = ImmutablePoint(0, 0);
print(a1 == a);//true
print(a == a2);//false 一個(gè)是常量棱诱,一個(gè)不是常量
print(a1 == a2);//false 一個(gè)是常量,一個(gè)不是常量
8.繼承
dart是單繼承機(jī)制
//Spacecraft定義見 7.類 class
class Orbiter extends Spacecraft {
num altitude;
Orbiter(String name, DateTime launchDate, this.altitude)
: super(name, launchDate);
}
var orbiter = Orbiter("google",DateTime.now(),100);
print(orbiter.name);
print(orbiter.launchData);
print(orbiter.altitude);
orbiter.describe();
9. Mixins
Mixins 跨多個(gè)類層次結(jié)構(gòu)重用代碼
Mixins are a way of reusing code in multiple class hierarchies
class Piloted {
int astronauts = 1;
void describeCrew() {
print('Number of astronauts: $astronauts');
}
}
//Spacecraft定義見 7.類 class
class PilotedCraft extends Spacecraft with Piloted {
// ···
}
//PilotedCraft同時(shí)有 Spacecraft 和 Piloted的成員變量和方法
var p = PilotedCraft("test", DateTime.now());
print(p is Spacecraft);//true
print(p is Piloted);//true
//不使用 class 關(guān)鍵字,只使用 mixin定義椎木,代表這一個(gè)類不能被實(shí)例化畜伐,沒有構(gòu)造函數(shù),只能被with使用
mixin Musical {
bool canPlayPiano = false;
bool canCompose =false;
bool canConduct = false;
void entertainMe() {
if(canPlayPiano) {
print("Playing piano");
} else if(canConduct) {
print("Waviing hands");
}else {
print("Humming to self");
}
}
}
10.接口與抽象類
Dart 語言并沒有提供 interface 關(guān)鍵字们颜,但是每一個(gè)類都隱式地定義了一個(gè)接口阻问。所有類都可以當(dāng)成接口被實(shí)現(xiàn)
class MockSpaceship implements Spacecraft {
// ···
}
抽象類
abstract class Describable {
void describe();
void describeWithEmphasis() {
print('=========');
describe();
print('=========');
}
}
class MyDescribable extends Describable {
@override
void describe() {
// TODO: implement describe
}
}
11.async/await 避開回調(diào)地獄
網(wǎng)上另外一個(gè)人寫的關(guān)于dart異步和消息機(jī)制的文章
【Dart學(xué)習(xí)】-- Dart之消息循環(huán)機(jī)制[翻譯]
使用async/await代替回調(diào),可以不使用回調(diào)就可獲取到異步操作后的結(jié)果哮塞,避免回調(diào)地獄刨秆,async/await可以理解成代替回調(diào)的語法糖
以創(chuàng)建文件,寫文件忆畅,讀取文件內(nèi)容 為例
//使用回調(diào)方式
void main() async {
var writeToFile = (File file) {
file.writeAsStringSync("${DateTime.now()}\n", mode: FileMode.append);
file.readAsString().then((content) {
print("$content");
}, onError: (e) {
print("e:$e");
});
};
var file = File("D://test.txt");
file.exists().then((bool exists) {
if (exists) {
writeToFile(file);
} else {
file.createSync();
writeToFile(file);
}
});
}
//使用async/await去掉回調(diào) 優(yōu)化代碼可讀性
void main() async {
var file = File("D://test.txt");
//Future<bool> exists() 下面如果沒有await返回的是Future<bool>衡未,如果有await返回的就是bool,異步的執(zhí)行結(jié)果
if(!await file.exists()) {
await file.create();
}
await file.writeAsString("${DateTime.now()}\n", mode: FileMode.append);
print("${await file.readAsString()}");
}
// async*和 yield
Stream<String> report(Iterable<String> objects) async* {
for (var object in objects) {
await Future.delayed(oneSecond);
yield 'flies by $object';
}
//await for
Future main() async {
// ...
await for (var request in requestServer) {
handleRequest(request);
}
// ...
}
12.異常 Exceptions throw rethrow
try catch final on 可以組合使用,常見有以下使用方式
//try catch(e) catch也可以把錯(cuò)誤堆棧打印了來缓醋,用法catch(e,stack)
try {
//todo
} catch(e,stack) {
print("catch: $e $stack");
}
//try cache(e) finally
try {
//todo
} catch(e) {
print("catch: $e");
} finally {
//todo
}
try {
//io操作
} on IOException catch (e) {
print('Could not describe object: $e');
} finally {
//todo
}
try {
//todo
} on IOException catch(e) {
print("Could not describe object: $e");
} catch(e) {
print("catch: $e");
} finally {
//todo
}
try {
//todo
} on IOException catch (e) {
//todo
} on Exception {
//todo
} finally {
//todo
}
rethrow throw 拋出異常
與java不同如失,throw可以拋出任何值類型的異常
如
拋出字符串異常 throw "this exception"
拋出數(shù)字異常 throw 10101
拋出 Exception子類對象 throw FormatException();
這樣也是可以的 throw FormatException
void misbehave() {
try {
throw "exception";
} catch (e) {
if (!canHandle(e)) rethrow;//把不處理的異常原樣拋出
handle(e);
}
}
異常與異步
try catch可以處理 await語句的異步異常
如果沒有await處理,try catch就不可以處理異步異常
Future<String> getExceptionFuture() {
var str = Future.delayed(Duration(seconds: 4),() => throw FormatException);
print("str type :${str.runtimeType}");
return str;
}
main() async {
try {
var result = await getExceptionFuture();
print("$result");
}catch(e) {
print("has exception runtimeType:${e.runtimeType} info:$e");
}
}
其他小知識點(diǎn)
使用可選參數(shù)(而不是使用重載)
class Person {
String name;
int age;
int height;
int weight;
Person({this.name, this.age, this.height,this.weight});
@override
String toString() {
return 'Person{name: $name, age: $age, height: $height, weight: $weight}';
}
}
void main() {
print(Person(name: "小明"));
print(Person(name: "小明",height: 170));
print(Person(name: "小明",height: 170,weight: 100));
}
字符串模板#
字符串模板使用
1.使用$并且沒有{} 例 "$x"
2.使用$并且有{} 例 "${person.name}"
//stringify(2, 3) should return '2 3'.
String stringify(int x, int y) {
return "$x $y";
}
操作符
x ??= y
如果x為null送粱,則給x賦值y,如果x不為null,則不賦值褪贵,不運(yùn)行y表達(dá)式(如y是一個(gè)函數(shù))
int a; // The initial value of a is null.
a ??= 3;
print(a); // <-- Prints 3.
a ??= 5;
print(a); // <-- Still prints 3.
value = x ?? y
如果x為null,則賦值y給value,否則賦值x給value
var nullValue;
print(nullValue ?? "this is y");//this is y
print("this is x" ?? "this is y");//this is x
條件屬性訪問 (避免空指針) ?.
條件屬性訪問葫督,用于避開空指針異常
myObject?.someProperty
相當(dāng)于
(myObject != null) ? myObject.someProperty : null
?.可以在同一個(gè)表達(dá)式中使用多次
myObject?.someProperty?.someMethod()
箭頭操作符 =>
箭頭操作符竭鞍,用于直接執(zhí)行可邊的表達(dá)式板惑,并返回右邊表達(dá)式的操作結(jié)果橄镜,用于替代函數(shù)的{}
例
bool hasEmpty = aListOfStrings.any((s) {
return s.isEmpty;
});
可以用 => 簡化成
bool hasEmpty = aListOfStrings.any((s) => s.isEmpty);
級聯(lián)操作符 ..
級聯(lián)操作符
myObject..someProperty..someMethod()
只有void修飾的方法可以使用級聯(lián),非void的都不可以使用級聯(lián)
class BigObject {
int anInt = 0;
String aString = '';
List<double> aList = [];
bool _done = false;
void allDone() {
_done = true;
}
//只有void修飾的方法可以使用級聯(lián)冯乘,非void的都不可以使用級聯(lián)
bool isDone() {
return _done;
}
}
BigObject fillBigObject(BigObject obj) {
if(obj is Null) {
return null;
}
return obj
..anInt = 1
..aString = 'String!'
..aList.add(3)
..allDone();
}
其他比較特殊的運(yùn)算符操作符
算術(shù)運(yùn)算符
assert(5 ~/ 2 == 2);// ~/ 除并取整
assert(5 % 2 == 1);// % 取模
類型判斷運(yùn)算符
as洽胶、is、is! 運(yùn)算符是在運(yùn)行時(shí)判斷對象類型的運(yùn)算符
// 類型檢查,如果類型成立裆馒,會(huì)智能轉(zhuǎn)換
if (emp is Person) {
emp.firstName = 'Bob';//不需要顯式轉(zhuǎn)換就可以使用Person的屬性和方法
}
//如果 emp 為 null 或者不為 Person 類型姊氓,則會(huì)拋出異常。
(emp as Person).firstName = 'Bob';
get 和 set
1.可以使用get set為私有成員變量提供對外訪問的方法喷好,如果get set不需要特殊處理翔横,成員變量定義成公開比較好
class MyClass {
int _aProperty = 0;
int get aProperty => _aProperty;
set aProperty(int value) {
if (value >= 0) {
_aProperty = value;
}
}
}
2.也給私有成員變量只使用 get,使變量對外只讀
class MyClass {
int _aProperty = 0;
int get aProperty => _aProperty;
}
3.只使用 get 可以使一個(gè)函數(shù)對外像一個(gè)屬性一樣訪問
class MyClass {
List<int> _values = [];
//函數(shù)對外像一個(gè)屬性一樣訪問,省去() var len = MyClass().count;
int get count {
return _values.length;
}
}
可選位置參數(shù)
//普通方法的參數(shù)列表
int sumUp(int a, int b, int c) {
return a + b + c;
}
//調(diào)用時(shí)三個(gè)參數(shù)都需要傳值梗搅,缺一不可
int total = sumUp(1, 2, 3);
//可選位置參數(shù)
//參數(shù)列表可以全部都是可選位置參數(shù),可以使用在構(gòu)造函數(shù)上
//可選位置參數(shù)需要用[]括起來禾唁,只能放在參數(shù)列表后面,可以有一個(gè)或多個(gè),
//方法調(diào)用時(shí)可選位置參數(shù)是可選的无切,不一定需要全部傳荡短,
//可選位置參數(shù)可以有默認(rèn)值,如果定義時(shí)沒有賦默認(rèn)值哆键,則為null
//可選位置參數(shù)不能與可選命名參數(shù)共存掘托,有可選位置參數(shù),不能有可選命名參數(shù)
int sumUpToFive(int a, [int b, int c=1, int d, int e]) {
int sum = a;
if (b != null) sum += b;
if (c != null) sum += c;
if (d != null) sum += d;
if (e != null) sum += e;
return sum;
}
int total = sumUptoFive(1, 2);
int otherTotal = sumUpToFive(1, 2, 3, 4, 5);
可選命名參數(shù)
//參數(shù)列表可以全部都是可選命名參數(shù),可以使用在構(gòu)造函數(shù)上
//可選命名參數(shù)需要放到參數(shù)列表后面籍嘹,同可選位置參數(shù)一樣可以有默認(rèn)值闪盔,也可能沒有,
//方法調(diào)用時(shí)可選命名參數(shù)是可選的辱士,不一定需要全部傳泪掀,
//與可選位置參數(shù)不同的是,可選命名參數(shù)需要使用時(shí)识补,要帶上參數(shù)名稱
//可選命名參數(shù)不能與可選位置參數(shù)共存族淮,有可選命名參數(shù),不能有可選位置參數(shù)
void printName(String firstName,String lastName,{String prefix,String suffix=''}) {
print("$prefix $firstName $lastName ${suffix ?? ""}");
}
printName("Poshmeister", "Moneybuckets",suffix:"IV");
printName("Poshmeister", "Moneybuckets",prefix:"prefix",suffix:"IV");
擴(kuò)展方法
dart在2.7版本支持了擴(kuò)展方法(kotlin也有相同的概念)
示例:
void main() {
print('10086'.parseInt());
print('10086'.parseInt().runtimeType);
print(Person(name: 'alibaba',age: 18,weight: 100,height: 200,country: 'zh').isChinesePeople());
}
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
// ···
}
extension PersonMethods on Person {
bool isChinesePeople () {
return 'zh' == country;
}
}
class Person {
String name;
int age;
int weight;
int height;
String country;
Person({this.name, this.age, this.weight, this.height,this.country});
}
dart代碼規(guī)范
- 關(guān)于dart代碼規(guī)范,可以查看阿里團(tuán)隊(duì)總結(jié)的規(guī)范
<< Flutter Go 開發(fā)規(guī)范第一版 >>