这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战
目录:
- 基础
- 变量
- 运算符
- 函数
- 对象
- 异步
一. 基础
1. 概念
- 在变量中可以放置的所有东西都是对象,而每个对象都是类的实例。无论数字、函数和 null 都是对象。所有对象都继承自 Object 类。
- 尽管 Dart 是强类型的,但类型声明是可选的,因为 Dart 可以推断类型。如果要明确说明不需要任何类型,请使用特殊类型 dynamic。
- Dart 支持通用类型,如
List<int>(整数列表)或List<dynamic>(任何类型的对象列表)。 - Dart 支持顶级函数(如 main()),以及绑定到类或对象(分别是静态方法(static)和实例(instance)方法)的函数。您还可以在函数(嵌套或局部函数)中创建函数。
- 类似地,Dart 支持顶级变量,以及绑定到类或对象(静态和实例变量)的变量。实例变量有时被称为字段或属性。
- 与 Java 不同,Dart 没有公开、保护和私有的关键字。如果标识符以下划线(_)开头,则该标识符对其库是私有的。
- 标识符可以以字母或下划线(_)开头,然后是这些字符加上数字的任何组合。
- 有时候,某事物是一个表达(expression)还是一个语句(statement)是很重要的,所以这两个词要准确。
- Dart 工具可以报告两种问题:警告和错误。警告只是表明您的代码可能不工作,但它们不会阻止您的程序执行。错误可以是编译时错误,也可以是运行时错误。编译时错误阻止了代码的执行;运行时错误导致代码执行时引发异常。
2. 内置类型
- numbers(int 和 double)
- strings
- booleans
- lists
- maps
- runes(用于在字符串中表示 Unicode 字符)
- symbols
3. 枚举
声明一个枚举类型:
enum Color { red, green, blue }
获取每个值的索引:
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
获取枚举中的所有值:
List<Color> colors = Color.values;
assert(colors[2] == Color.blue);
二. 变量
1. var
类似于 JavaScript 中的 var,它可以接收任何类型的变量,但最大的不同是 Dart 中 var 变量一旦赋值,类型便会确定,则不能再改变其类型。(有差异是因为 Dart 本身是一个强类型语言,任何变量都是有确定类型的)
var t;
t = "pany";
// 下面代码在 Dart 中会报错,因为变量 t 的类型已经确定为 String
t = 1000;
2. dynamic 和 Object
dynamic 与 var 一样都是关键词,声明的变量可以赋值任意对象。
Object 是 Dart 所有对象的根基类,也就是说所有类型都是 Object 的子类(包括 Function 和 Null),所以任何类型的数据都可以赋值给 Object 声明的对象。
而 dynamic 与 Object 相同 之处在于,他们声明的变量可以在后期改变赋值类型。
dynamic a;
Object b;
a = "a";
b = 'b';
a = 1000; // 没有问题
b = 1000; // 没有问题
dynamic 与 Object 不同 的是,dynamic 声明的对象编译器会提供所有可能的组合,而 Object 声明的对象只能使用 Object 的属性与方法,否则编译器会报错。
dynamic a;
Object b;
main() {
a = "";
b = "";
printLengths();
}
printLengths() {
print(a.length); // no warning
print(b.length); // warning:The getter 'length' is not defined for the class 'Object'
}
dynamic 的这个特点使得我们在使用它时需要格外注意,这很容易引入一个运行时错误。
3. final 和 const
如果您从未打算更改一个变量,那么使用 final 或 const。两者区别在于:const 变量是一个编译时常量(引用与值都不能改变),final 变量在第一次使用时被初始化(值能改变)。
注意:实例变量可以是 final,但不能是 const
// 可以省略 String 这个类型声明
final a = "a"; // final String str = "a";
const b = "b"; // const String str1 = "b";
const 关键字不只是声明常量变量,你还可以使用它来创建常量值,以及声明创建常量值的构造函数,任何变量都可以赋一个常量值:
var a = const [];
final a = const [];
const a = []; // 等同于:const a = const [],因为后面的 const 是可选的
4. 显式声明要推断的类型
String name = 'pany';
String name = true; // 这句会报错
5. 默认值
未初始化的变量的初始值为 null,甚至具有数字类型的变量最初也是 null,因为数字就像 Dart 中的其他东西一样是对象。
生产环境中 assert() 调用被忽略。开发环境中 assert(condition) 的 condition 条件不为真时抛出异常
int i;
assert(i == null);
三. 运算符
JavaScript 里面没有的三个常用运算符:
1. ?. (根据条件访问成员)
foo?.bar = 4;
foo?.bar 获取 bar 属性,如果 foo 为空则返回 null
2. .. (联级)
注意:严格地说,级联 “..” 表示法不是运算符,这只是 Dart 语法的一部分。
List<int> listInt = List()
..add(0)
..add(1)
..add(2)
..removeAt(1);
print(listInt); // [0, 2]
3. ??
var a = b ?? 'pany';
如果 b 为 null,则返回 'pany',否则返回 b 的值
四. 函数
1. 函数声明
bool isNoble(int a) {
return _nobleGases[a] != null;
}
2. 函数作为参数
typedef bool CALLBACK();
// 不指定返回类型,此时默认为 dynamic,不是 bool
isNoble(int a) {
return _nobleGases[a] != null;
}
void test(CALLBACK cb){
print(cb());
}
// 报错,isNoble 不是 bool 类型
test(isNoble);
3. 函数声明的简写
bool isNoble(int a) => _nobleGases[a] != null ;
4. 函数表达式
var say = (str) {
print(str);
};
say("hi world");
5. 可选的位置参数
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
say('Bob', 'Howdy');
say('Bob', 'Howdy', 'smoke signal');
- 第一句结果是:Bob says Howdy
- 第二句结果是:Bob says Howdy with a smoke signal
6. 可选的命名参数
// 设置 bold 和 hidden 标志
void enableFlags({bool bold, bool hidden}) {
// ...
}
enableFlags(bold: true, hidden: false);
注意:不能同时使用位置参数和命名参数。
7. 词法闭包
Function makeAdder(num addBy) {
return (num i) => addBy + i;
}
void main() {
var add2 = makeAdder(2);
var add4 = makeAdder(4);
print(add2(3) == 5); // true
print(add4(3) == 7); // true
}
makeAdder() 捕获变量 addBy。无论返回的函数到哪里,它都会记住 addBy。
8. 判断函数相等
void foo() {} // 顶级函数
class A {
static void bar() {} // 静态方法
void baz() {} // 实例方法
}
void main() {
var x;
// 比较顶级函数
x = foo;
print(foo == x); // true
// 比较静态方法
x = A.bar;
print(A.bar == x); // true
// 比较实例方法
var v = A();
var w = A();
var y = w;
x = w.baz;
print(y.baz == x); // true
print(v.baz != w.baz); // true
}
9. 如果没有指定返回值,那么函数返回 null
foo() {}
print(foo() == null); // true
五. 对象
1. 判断两个对象是否相等:identical
- 这里使用常量构造函数举例(使用常量构造函数创建编译时常量,请将 const 关键字放在构造函数名之前)
- 构造两个相同的编译时常量会生成一个单一的、规范的实例(如下例)
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
assert(identical(a, b)); // 它们是同一个实例
2. 获取对象的类型:runtimeType
print('对象a的类型是:${a.runtimeType}');
3. 构造函数
注意:1. 构造函数不继承 2. 没有构造函数的类会有默认构造函数(没有参数,没有名称)
class Point {
num x, y;
Point(this.x, this.y); // 构造函数
}
命名构造函数:
class Point {
num x, y;
Point(this.x, this.y);
Point.origin() { // 命名构造函数
x = 0;
y = 0;
}
}
当然还有其他的构造函数,例如“工厂构造函数”、前文提到的“常量构造函数”等等...
六. 异步
Dart 类库有非常多的返回 Future 或者 Stream 对象的函数。 这些函数被称为 异步函数。
1. Future
Future 与 JavaScript 中的 Promise 基本一致。
1.1. Future.then
// 用 Future.delayed 模拟一下耗时任务
Future.delayed(new Duration(seconds: 2),(){
return "hi world!";
}).then((data){
print(data); // 两秒后打印 "hi world!"
});
1.2. 捕获异常:
1.2.1 Future.catchError:
Future.delayed(new Duration(seconds: 2),(){
// return "hi world!";
throw AssertionError("Error");
}).then((data){
// 执行成功会走到这里
print("success");
}).catchError((e){
// 执行失败会走到这里
print(e);
});
1.2.2 onError:
Future.delayed(new Duration(seconds: 2), () {
// return "hi world!";
throw AssertionError("Error");
}).then((data) {
print("success");
}, onError: (e) {
print(e);
});
1.3. Future.whenComplete
使用场景:在网络请求前弹出加载对话框,在请求结束后关闭对话框。
Future.delayed(new Duration(seconds: 2),(){
// return "hi world!";
throw AssertionError("Error");
}).then((data){
// 执行成功会走到这里
print(data);
}).catchError((e){
// 执行失败会走到这里
print(e);
}).whenComplete((){
// 无论成功或失败都会走到这里
});
1.4. Future.wait
Future.wait([
// 2秒后返回结果
Future.delayed(new Duration(seconds: 2), () {
return "hello";
}),
// 4秒后返回结果
Future.delayed(new Duration(seconds: 4), () {
return " world";
})
]).then((results){
print(results[0]+results[1]); // 4秒后打印 “hello world”
}).catchError((e){
print(e);
});
2. async/await
async/await 与 JavaScript 中的 async/await 用法一模一样,笔记到这里就跳过了。
无论是在 JavaScript 还是 Dart 中,async/await 都只是一个语法糖,编译器或解释器最终都会将其转化为一个 Promise(Future)的调用链。
3. Stream
Stream 也是用于接收异步事件数据,和 Future 不同的是,它可以接收多个异步操作的结果(成功或失败)。也就是说,在执行异步任务时,可以通过多次触发成功或失败事件来传递结果数据或错误异常。 Stream 常用于会多次读取数据的异步任务场景,如网络内容下载、文件读写等。
Stream.fromFutures([
// 1秒后返回结果
Future.delayed(new Duration(seconds: 1), () {
return "hello 1";
}),
// 抛出一个异常
Future.delayed(new Duration(seconds: 2),(){
throw AssertionError("Error");
}),
// 3秒后返回结果
Future.delayed(new Duration(seconds: 3), () {
return "hello 3";
})
]).listen((data){
print(data);
}, onError: (e){
print(e.message);
},onDone: (){
});
// 依次输出:
I/flutter (17666): hello 1
I/flutter (17666): Error
I/flutter (17666): hello 3