[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'
复制代码
上面打印的时候访问不到 {} 内的变量,因此报错了