[toc]javascript
1. var 關鍵字html
可使用 var 進行聲明一個常量java
main() {
var str = 'abc';
str = '123';
print(str); // -> 123
}
複製代碼
使用 var 關鍵詞進行聲明的時候,dart 會自動推斷出 當前變量的類型,若是在變量聲明的時候沒有進行賦值,那麼該類型就是動態的,相似於 TS 的 any。在類型推斷上跟 TypeScript
是一致的。typescript
var str = 'abc';
str = 123;
print(str); // -> Error: int 值不能分配給 String 類型的變量
// 聲明變量時不賦值
var some;
some = 'abc';
str = 123;
print(some); // -> 123
複製代碼
2. final 關鍵字express
final 聲明一個常量,只能被初始化一次。並且在初始化的時候必須賦值。其相似於 js 中的 const。final 的變量時運行時初始化編程
final str = 'abc';
str = 'def';
print(str); // -> Setter not found: 'str'.
// or
final some;
print(some); // -> must be initialized
複製代碼
final list = [1, 2];
list[0] = 3;
print(list); // -> [3, 2]
複製代碼
3. const 關鍵字api
const 與 final 相似,也是表示一個常量,但其表示的是一個編譯時的常量,這個常量既能夠是一個變量也能夠是一個值。其特性與 final 類似,可是有很大差異,其在編譯時初始化。數組
var some = 'abc';
final some2 = some;
print(some); // -> abc 沒問題
var str = 'abc';
const str2 = str;
print(str2); // -> Error: Not a constant expression
複製代碼
const list = [1, 2];
list[0] = 3;
print(list); // -> Cannot modify an unmodifiable list
複製代碼
var list = const[1, 2];
list[0] = 3; // Error: Cannot modify an unmodifiable list
list = [3, 4];
print(list); // -> [3, 4] list 不是常量,仍然能夠被從新賦值
複製代碼
final list = const[1, 2];
list[0] = 3; // -> Error: Cannot modify an unmodifiable list
list = [3, 4]; // -> Error: Setter not found: 'list'
複製代碼
4. 顯式的聲明變量緩存
dart 支持靜態類型,直接靜態聲明的話,即在聲明時候就規定了其類型,不能賦值非指定類型的值。bash
String str = 'abc';
str = '123';
str = 2333; // Error: int 類型不能賦值給 String 類型的變量
複製代碼
若是不使用靜態聲明,則默認的類型爲 dynamic
var bar = 123;
print(bar is dynamic); // true
複製代碼
*注:關鍵字 is 用於類型判斷,返回 bool
5. 能夠類型聲明與變量聲明關鍵字一塊兒使用
final int number = 123;
number = 233; // Error: Setter not found: 'number'.
const bool flag = true;
flag = false; // Error: Setter not found: 'flag'.
var String str = 'abc';
str = 'edg';
複製代碼
6. 默認值
若是在定義變量的時候沒有初始化值,那麼他的初始值是 null。除了常量,由於常量定義時必須有初始值
var some; // -> null
bool flag; // -> null
int number; // -> null
String str; // -> null
Object obj; // -> null
final namic; // Error: must be initialized
複製代碼
String str = 'abc';
複製代碼
String name = '小明';
print('${name} 是個直男'); // -> 小明是個直男
複製代碼
與 js 的反引號不一樣,dart 須要使用三個單引號或雙引號表示多行字符串
String breakStr = '''
這是一段,
多行字符串
'''
複製代碼
數字類型有三種,int 、double 和 num
int number = 123;
num = 1.1; // Error:不能將 double 類型的值賦值給 int 類型
複製代碼
double number = 1.1;
number = 2; // Error: 不能將 int 類型的值賦值給 double 類型
複製代碼
int 與 doubled 都是 num 的子類,其至關於 int 與 double 的聯合類型。
num number = 1;
number = 2.33; // 2.33
複製代碼
與其餘編程語言同樣,她只有兩個值。true or false
bool flag = 1 == 1;
print(flag); // -> true
複製代碼
bool flag = 1 == '1';
print(flag); // -> false
複製代碼
var flag = 1;
if(flag) {
print('真');
} else {
print('假');
}
// Error: 不能將 int 類型的值賦值爲 bool 變量
複製代碼
上面的代碼在 js 中不會有問題,可是在 dart 中會報錯,由於 flag 不是布爾值
列表,相似於 js 中的 Array
var arr1 = new List();
print(arr1); // -> [];
var arr2 = [1, 2, '3', {}];
print(arr2); // -> [1, 2, '3', {}];
複製代碼
ts
同樣,指定 list 的成員類型List<int> arr = [1, 2];
arr = [1, 2, '3']; // Error: String 不能分配給 int 類型的變量
複製代碼
var arr = new List<int>();
// 添加成員
arr.add(1); // -> [1]
// 添加多個成員, 相似於 js 的 concat,但只能接受一個參數
arr.addAll([2, 3]); // -> [1,2,3]
// 獲取索引, 相應的還有 lastIndexOf
var index3 = arr.indexOf(3);
print(index3); // -> 2
// 移除某個成員,只會移除匹配到的第一個成員
arr.remove(1); // -> [2,3]
// 清空 List
arr.clear(); // -> []
複製代碼
List<int> arr = [1,2,3];
Map<int, int> arrMap = arr.asMap();
print(Map); // -> {0: 1, 1: 2, 2: 3}
複製代碼
須要注意的是,在將 list 轉化爲 Map 的時候,list 必須指定成員類型
鍵爲惟一的鍵值對,鍵與值能夠是任意類型。特性有點相似 js 的 Object
.
語法var map = new Map();
map['a'] = 'a';
map[1] = 1;
map[null] = null;
print(map); // -> {a: a, 1: 1, null: null}
// 訪問不存在的成員會返回 null
print(map['c']); // -> null
複製代碼
Map<int, int> map = {
1: 1,
2: 2,
};
print(map); // -> 2
複製代碼
Map<int, int> map = {
1: 1,
2: 2,
};
print(map.length); // -> 2
複製代碼
內置方法
Map<int, int> map = {
1: 1,
2: 2,
};
// 添加成員
map.addAll({3: 3}); // -> {1: 1, 2: 2, 3: 3}
// 移除某個成員
map.remove(2); // -> {1: 1, 3: 3}
// 清空成員
map.clear(); // -> {}
複製代碼
Map<String, int> map = {
'a' :1,
};
// 是否有該 key
bool hasKey = map.containsKey('a');
// 是否有該 value
bool hasValue = map.containsValue(2);
print(hasKey); // -> true
print(hasValue); // -> false
// 是不是一個空對象
print(map.isEmpty); // -> false
複製代碼
var map = {
1: 1,
1: 1,
3: 3
};
// 移除符合的某一條件的屬性
var filterAttr = map.removeWhere((key, value){
return value < 2;
});
print(map); // {3: 3}
複製代碼
map 的屬性是可保證順序的
js 很是坑的一點是,在遍歷一個對象的時候,並不能保證屬性的順序。可是 dart 能夠。
js 中遍歷一個對象的時候
var obj = {
3: 'a',
2: 'b',
1: 'c',
};
var arr = [];
for(var key in obj){
arr.push(key);
}
console.log(arr); // [1, 2, 3]
複製代碼
上面的代碼中,我遍歷了一個對象,想將其 key 依次 push 進一個數組,我指望獲得的是 [3, 2, 1],可是的獲得的確是 [1, 2, 3], 由於 javascript 並不能保證 object 屬性的順序
dart 中遍歷一個 map
Map<int, String> map = {
3: 'a',
2: 'b',
1: 'c',
};
List<int> arr = [];
map.forEach((key, value){
arr.add(key);
});
print(arr); // [3, 2, 1]
複製代碼
map 會按照定義時的屬性的位置順序進行遍歷,正確的打印出 [1, 2, 3]
Function 在 javascript 中是一等公民,既能夠當作方法賦值給變量,也能夠做爲參數,也能夠將實例的函數類型的屬性當作方法使用。Dart 的Function 與 它相似
int sum(int x, int y) {
return x + y;
}
sum(1, 2); // 3
複製代碼
上面是一個求和方法,接受兩個參數,規定必須是int 類型,其 returns 類型的聲明是放在最前面的,與 typeScript 不一樣,typeScript 是 在函數頭後面聲明
箭頭函數使用方法與 js 同樣
int sum(int x, int y) => x + y;
複製代碼
與 ts 不一樣的是,dart 不是使用
?
來標記可選參數,而是使用[]
int sum(int x, int y, [int z]) {
if(z == null) {
return x + y;
}
return x + y + z;
};
sum(1, 2); // -> 3
sum(1, 2, 3) // -> 6
複製代碼
默認參數與 js 使用方法同樣。在定義方法的時候直接
參數 = value
, 可是默認值只能加給可選參數
int sum(int x, int y, [int z = 3]) {
return x + y;
};
sum(1, 2); // -> 6
複製代碼
若是給必選參數加默認值會報錯
int sum(int x, int y, int z = 3) {
return x + y;
};
// Error: Non-optional parameters can't have a default value.
複製代碼
能夠給參數指定名字,未指定名字的參數爲未知參數。有些時候在使用可選參數的時候並不明確參數的定義,可使用命名參數,在使用方法的時候必須加上可選參數的名字
int sum(int x, int y, {int z: 3}) {
return x + y + z;
};
sum(1, 2, z: 3); // -> 6
sum(1, 2, 3); // -> Error: 應該有兩個位置參數,但發現了三個
複製代碼
匿名函數就是聲明時沒有進行命名的函數
List<int> arr = [1, 2, 3, 4, 5];
arr.forEach((v) {
print(v); // 1, 2, 3, 4, 5
});
複製代碼
上面的代碼將一個打印的匿名方法傳進forEach
dart 的每個函數都有返回值,若是沒有 return 返回值則自動 return 一個 null
add(int x) {}
var autoNull = add(1);
print(autoNull); // null
複製代碼
若是不肯定該變量的值是什麼類型, 可使用 dynamic,表示該類型的類型是動態的,相似於
TspeScript
中的 any。與 typeAcript 同樣,並不推薦使用它,由於錯誤的類型使用不會在編輯時報錯可是會在運行時報錯
dynamic some = 'abc';
some = some + 123; // 編譯時不會報錯,可是運行時會報錯
複製代碼
Obiect 表示任意類型,object之因此可以被賦值爲任意類型的緣由,由於全部的類型都派生自 Obiect.
Obiect some = 'str';
some = 1;
some = true;
複製代碼
每一個對象都是一個類的實例,全部的類都繼承於 Object。 基於 Mixin 的繼承 意味着每一個類(Object 除外) 都只有一個超類,一個類的代碼能夠在其餘 多個類繼承中重複使用。class 不能夠定義在 main 主函數中
class Person {
String name = '湯姆';
int age = 8;
}
main() {
var tom = new Person();
print(tom.name); // '湯姆'
}
複製代碼
跟 js 不同,dart 的構造函數是一個與 class 同名的函數。
class Person {
String name;
int age;
// 與 class 同名的構造函數
Person(String name, int age){
this.name = name;
this.age = age;
}
void sayHi() {
print('my name is ${this.name}, 今年${this.age}歲');
}
}
main() {
Person xiaohong = new Person('小紅', 8);
xiaohong.sayHi(); // -> my name is 小紅, 今年8歲
}
複製代碼
class Person {
String name;
Person(){
this.name = '我是誰';
}
}
class People extends Person {
int age;
People() {
this.age = 18;
}
}
People xiaohong = new Person();
print(xiaohong.name); // 默認調用了父類的構造函數,因此 name 屬性有值
print(xiaohong.age);
複製代碼
在一個類裏面能夠建立多個構造函數,其命名方式爲
構造函數名.xxx
建立實例的時候,能夠選擇不一樣的構造函數進行建立。
class Person {
String name;
String age;
Person(this.name, this.age);
Person.fromMap(Map<String, String> param) {
this.name = param['name'];
this.age = param['age'];
}
sayHi() {
print('$name, 今年$age歲');
}
}
main() {
// 使用普通的構造函數建立實例
Person xiaohong = new Person('小紅', '8');
xiaohong.sayHi(); // 小紅, 今年8歲
// 使用命名構造函數 fromMap 來建立實例
Map<String, String> userInfo = {
'name': '小明',
'age': '6'
};
Person xiaoming = new Person.fromMap(userInfo);
xiaoming.sayHi(); // 小明, 今年6歲
}
複製代碼
:super.父類的命名構造函數名
就能夠手動調用父類的構造函數, 調用順序爲先調用父類的函數再執行本身的構造函數
class Person {
String name;
Person.initName(){
this.name = '我是誰';
print(1);
}
}
class People extends Person {
int age;
People():super.initName() {
this.age = 0;
print(2);
}
sayHi() {
print('$name, 今年$age歲');
}
}
main() {
People xiaohong = new People();
// -> 1
// -> 2
xiaohong.sayHi(); 我是誰, 今年0歲
}
複製代碼
構造函數的聲明中能夠調用其餘的構造函數,此時,該構造函數沒有函數體
class Person {
String name;
Person.name(String name) {
this.name = name;
}
// 構造函數 init 調用了構造函數 name
Person.init(String name): this.name(name);
sayHi() {
print('我叫$name');
}
}
main() {
Person xiaohong = new Person.init('小紅');
xiaohong.sayHi(); // 我叫小紅
}
複製代碼
若是一個構造函數並不老是返回一個新的對象,則使用 factory 來定義 這個構造函數。下面的類是爲了建立 person 並避免建立重名的 person,建立新的對象後緩存起來,若是建立重名的就從已有的對象裏面去取。
工廠方法構造函數裏不能訪問 this
class Person {
String name;
static final Map<String, Person> personMap = <String, Person>{};
factory Person(String name) {
if (personMap.containsKey(name)) {
print('catch person');
return personMap[name];
} else {
print('new person');
Person newPerson = new Person.create(name);
personMap[name] = newPerson;
return newPerson;
}
}
Person.create(String name) {
this.name = name;
}
sayHi() {
print('my name is ${name}');
}
}
main() {
Person xiaohong = new Person('小紅'); // -> new person
xiaohong.sayHi(); // -> my name is 小紅
Person xiaoming2 = new Person('小紅'); // -> catch person
xiaoming2.sayHi(); // -> my name is 小紅
}
複製代碼
上面建立的第二個小紅,其實就是取得第一個小紅,並無建立新對象。
this.xxx
, 當未聲明變量的時候會自動尋找 this 中有沒有該屬性class Person {
String name;
int age;
Person(String name, int age){
this.name = name;
this.age = age;
}
void sayHi() {
// 這裏手動定義的 age 優先級比 this.age 高
var age = 99;
// 沒有定義 name,會自動尋找 this.name
print('my name is ${name}, 今年${age}歲');
}
}
main() {
var xiaohong = new Person('小紅', 8);
xiaohong.sayHi(); // -> my name is 小紅, 今年99歲
}
複製代碼
setters 與 getters 是用來設置與獲取屬性值的函數,通常狀況下不須要手動設置,由於每一個實例成員都有隱性的 getter 方法,非 final 的成員也會有隱性的 setter 方法。若是顯式的去聲明的話,使用
set
與get
關鍵字set 與 get 函數中都不能夠訪問本身,不然會死循環
set 函數中能夠修改其餘成員的值
下面的例子顯式的聲明瞭 Point 的 y 的 set 與 get 函數
class Point {
int x;
Point(this.x);
int get y => x + 3;
set y(int value) => x = value;
}
main() {
Point point = new Point(5);
point.y = 10;
print(point.x);
print(point.y);
}
複製代碼
抽象類不能實例化,須要繼承他的子類去實例。抽象函數是隻有函數名可是沒有函數圖,須要子類去實現具體的功能
抽象函數用分號代替函數體
抽象函數必須在子類中被實現
abstract class Person {
sayHi();
}
class Student extends Person {
sayHi() {
print("I'm a student");
}
}
main() {
Person student = new Student();
student.sayHi();
}
複製代碼
dart 沒有提供 interface 關鍵字用來聲明接口,
可是可使用implements
關鍵字將 class 當作接口使用
/// 一個抽象類 Person
abstract class Person {
String name;
}
/// 使用 implements 關鍵字把 Person 當作接口使用
/// Teacher 必須含有 Person 的格式
class Teacher implements Person {
String name;
}
main() {
var xiaohong = new Teacher();
print(xiaohong); // -> Instance of 'Teacher'
}
複製代碼
在使用接口的時候,並非像 typescript 同樣必須嚴格一致,dart 裏只須要實現接口內的結構就不會報錯,能夠有本身的成員。
/// 做爲 Person 接口的實現,Teacher 多了個 age 成員,可是沒有問題
class Teacher implements Person {
String name;
int age;
}
複製代碼
可使用 try catch 語句來捕獲異常
try {
dynamic foo = true;
print(foo++); // 運行時錯誤
} catch (e) {
print('錯誤類型: ${e.runtimeType}'); // -> 錯誤類型: NoSuchMethodError
} finally {
print('不管有沒有異常都會執行');
}
複製代碼
可使用 throw 主動拋出錯誤,也能夠在 catch 裏使用 rethrow 關鍵字繼續將捕獲到的錯誤拋出
throw 拋出的異常能夠是任意類型
try {
throw '一個字符串錯誤'; // 主動拋出一個 String 類型的錯誤
} catch (e) {
print('錯誤類型: ${e.runtimeType}.'); // 錯誤類型: String.
rethrow; // 使用 rethrow 關鍵字繼續將錯誤拋出
}
複製代碼
可使用 on 聲明要在塊裏捕獲的錯誤類型,順序從上往下相似於 switch;
try {
throw '一個字符串錯誤';
} on String catch(e) {
print('捕獲 String 類型異常: ${e.runtimeType}.'); // 捕獲 String 類型異常: String
}catch (e) {
print('捕獲全部類型異常: ${e.runtimeType}.');
}
複製代碼
dart 可使用
is
關鍵字進行類型判斷。比 typescript 與 javascript 的typeof
更加方便、準確
var str = 'string';
print(str is String); // true
複製代碼
泛型又稱爲參數化類型,使用方法與 typescript 類型,在具體的類型後面加
<類型>
。dart 還能夠用字面量的形式在以前面使用泛型進行註解
List<bool> arr = [1];
// Error: 參數類型'int'不能分配給參數類型'bool'
var array = <int>[1, 2, 3];
arr.array('4');
// Error: 參數類型'String'不能分配給參數類型'int'
複製代碼
上面兩種泛型的使用方式都能對類型進行保護
var arr = new List<int>();
arr.addAll([1, 2, 3]);
print(names is List<int>); // -> true
複製代碼
泛型函數能夠在如下地方使用泛型
返回值
參數
函數內變量
T getFirstItem<T>(List<T> arr) {
T firstItem = arr[0];
return firstItem;
};
var first = getFirstItem<int>([1, 2, '3']);
print(arr); // Error: 不能將參數類型 String 分配給 參數類型 int
}
複製代碼
上面代碼由於 getFirstItem 在使用的時候聲明泛型是 int,可是傳入的 List 內卻有 '4',因此報錯了
可使用 extends 關鍵字來縮小泛型的類型範圍,使用方式與 ts 同樣
能夠給方法的類型起個名字,用來定義方法的樣子。相似於 ts 的
type
,不過只能用於方法
typedef int Add(int x, int y);
main() {
Add add = (x, y) => x + y;
var sub1 = add(1, '2'); // Error: 不能將 String 類型的參數賦值給 int 類型的參數
var sub2 = add(1, 2); // -> 3
print(sub2 is int); // -> true
}
複製代碼
上面定義了一個名爲Add
的方法類型別名,其做爲方法 add 的類型,由於 sub1 傳入的參數類型與 Add 定義的不符,因此報錯了。 由於 Add 定義了 add 方法的返回值是 int 類型,因此 (sub2 is int)
爲 true
建立
pubspec.yaml
文件來聲明該項目須要的依賴包,在 使用 put 命令拉取依賴包的時候會生成一個.packages
文件,該文件將項目所依賴的每一個程序包名稱映射到系統緩存中的相應包
name: myApp
dependencies:
js: ^ 0.3.0
intl: ^ 0.12.4
複製代碼
dart 使用
pub
工具來管理 package 和 assets。在下載 Dart 的時候會帶有 pub不需額外下載
pub get # 獲取依賴項
pub upgrade # 更新依賴項
pub run [some script] [args] # 執行某個腳本
複製代碼
引入內置的庫: dart: 庫名
import 'dart:io';
複製代碼
可使用 import 語句從 package 引入某個模塊,要加 package 聲明
// 使用 import as 語句引入某個模塊
import 'package:js/js.dart' as js;
// 引入 js 模塊的一部分 - anonymous
import 'package:js/js.dart' show anonymous;
複製代碼
引入庫除此以外的其餘部分
// 引入 js 庫除 anonymous 的部分
import 'package:js/js.dart' hide anonymous;
複製代碼
直接使用相對路徑引入本身的模塊
import '../someLib.dart';
複製代碼
dart 直接提供了按需加載的語法,可讓應用在須要的時候再加載庫。好比說一個頁面中可能有不一樣的狀態,不一樣的狀態下可能須要不一樣的依賴庫,這時候使用按需加載的話就能減小沒必要要的資源與性能浪費。這很是很是的方便!想一想一下在寫 js 業務的時候,在一個有多種狀態的頁面須要把每個狀態的依賴加載下來。
想要延遲加載一個庫,使用 deferred
關鍵字來導入
import 'package:js/js.dart' deferred as JS;
複製代碼
使用的時候,異步的方式調用庫的 loadLibrary 函數就能夠, 異步語法與 js 類似
int flag = true;
getJs() async {
await Js.loadLibrary(); // 引進 JS ku
// 下面的代碼可使用 JS 庫了
}
複製代碼
當延遲加載庫時,整個庫內的內容都是爲加載過來的,包括庫內的類型聲明。因此若是想在使用庫前使用庫內的類型聲明,能夠把這個庫的類型聲明抽出來單獨做爲一個文件。
包文件:person.dart
library person; // 指定庫名,用來生成文檔。
class Teacher {
String name;
Teacher(this.name) {
print('我是 ${name} 老師');
}
}
class Student {
String name;
Student(this.name) {
print('我是學生 ${name}');
}
}
複製代碼
上面建立了一個 person.dart
文件,裏面定義了兩個類 teacher 和 student,不須要導出,使用的時候直接引進來就行。 library 通常不須要手動聲明,由於 dart 會自動生成惟一的庫標識,除非想要生成庫 文檔。
import './common/person.dart' as person;
main() {
var xiaohong = new person.Teacher('小紅'); // -> 我是 小紅 老師
var xiaoming = new person.Student('小明'); // -> 我是學生 小明
}
複製代碼
也可使用 show
關鍵字只導入某一個部分
import './common/person.dart' show Teacher;
main() {
var xiaohong = new Teacher('小紅'); // -> 我是 小紅 老師
}
複製代碼
有時候一個庫很大,或者一個庫裏須要有公用的變量或方法,可使用 part 語法進行拆分。
仍是上面 person 的例子,假如 Teacher 類與 Student 類都有一個 sayHi 的函數,而這兩個類的 sayHi 函數的邏輯是同樣的,這裏就能夠將這個邏輯單獨抽出來放到一個獨立的文件中去實現。
person 庫:
library person;
part 'commonSayHi.dart'; // 聲明sayHi的實現 - commonSayHi 的路徑
class Teacher {
String name;
Teacher(this.name);
sayHi() {
commonSayHi(this.name);
}
}
class Student {
String name;
Student(this.name);
sayHi() {
commonSayHi(this.name);
}
}
複製代碼
commonSayHi:
part of 'person.dart'; // 聲明這是用於 person.dart 文件的實現
commonSayHi (name) {
print("Hello, I'm ${name}");
}
複製代碼
使用:
import './common/person.dart' show Teacher;
main() {
var xiaohong = new Teacher('小紅');
xiaohong.sayHi(); // Hello, I'm 小紅
}
複製代碼
強類型,靜態類型檢查
dynamic
靜態做用域 & 擁有塊級做用域
dart 跟 js 同樣是靜態做用域。可是 Dart 有塊級做用域,在
{}
外訪問不到{}
內的代碼。
{
var a = 1;
}
print(a); // Error: Getter not found: 'a'
複製代碼
上面打印的時候訪問不到 {} 內的變量,因此報錯了