重要概念:
- 所有能够使用变量引用的都是对象, 每个对象都是一个类的实例。在 Dart 中 甚至连 数字、方法和
null
都是对象。所有的对象都继承于 Object 类。 - 指定的类型为静态类型,没有指定类型的变量的类型为 dynamic。
- Dart 在运行之前会先解析你的代码。你可以通过使用 类型或者编译时常量来帮助 Dart 去捕获异常以及 让代码运行的更高效。
- Dart 没有 public、 protected、 和 private 关键字。如果一个标识符以 (_) 开头,则该标识符 在库内是私有的。
- 能够使用变量引用的都是对象,包括数字、方法、null等,默认值都为null
- final和const:const是编译时常量,也是final。实例变量使用final,使用const则需要添加static:
static const
。const修饰的构造函数创建的对象所有属性不可变。
// 类外面
const nameOut = "name_out"; // 不可变
final nameOut2 = "name_out2"; // 必须进行初始化,不可变
// static const nameOut3 = "name_out3"; 错误用法,static不能用在类外面
class TestClass {
/*
* static 标识一个成员属于类而不是对象
* final 必须初始化,初始化后值不可变,编译时不能确定值
* const 编译时可确定,并且不能修改
* */
final name; // 需要在构造方法里初始化
static const name2 = 'name2'; // 只有静态变量才能被定义为const
final name3 = 'name3'; // 定义时初始化
TestClass(this.name);
void test() {
const name4 = "name4"; // 不可变
var name5 = "name5"; // 可变
final name6 = "name6"; // 不可变
// static 不可用在方法里
// static final name7 = "name7";
// name4 = "name"; // 不可更改
name5 = "name";
// name6 = "name"; // 不可更改
print("测试:$name4, $name5, $name6");
}
}
知识点:
- 数值numbers : num/int/double
字符串与数字之间的转换:
var one = int.parse('1');
var onePointOne = double.parse('1.1');
String oneAsString = 1.toString();
String piAsString = 3.14159.toStringAsFixed(2);
- 字符串Strings(UTF-16编码)
- 使用单引号或者双引号来创建。Dart 字符串是 UTF-16 编码的字符序列。
- 插值操作:
$变量
、${表达式}
,表达式的结果为一个对象,则 Dart 会调用对象的 toString() 函数来获取一个字符串。 - == 操作符判断两个对象的内容是否一样。
- 使用三个单引号或者双引号也可以 创建多行字符串对象。
- 使用r'字符串内容'创建原始值字符串,其中的\n也是字符串内容,而不是换行。
- 字符串编译时常量可以插入常量,不能插入变量。
- 布尔值Booleans
类型bool,包含true/false。true为true,其他所有值均为false。 - 列表Lists
类型List,使用[],可使用const来定义一个不可变的List(编译时常量)。 - 键值对象Maps
类型Map,使用{},使用 .length 来获取 map 中键值对的数目,可使用const来定义一个不可变的Map(编译时常量)。<String, Logger>{}
- Runes
字符串的UTF-32编码值。
使用codeUnitAt 和 codeUnit 属性返回 16-bit code units。
使用runes返回32-bit Unicode。
var clapping = '\u{1f44f}';
print(clapping); // 👏
print(clapping.codeUnitAt(0)); // 55357
print(clapping.codeUnits); // [55357, 56399]
print(clapping.runes.toList()); // (128079)、 toList(): [128079]
Runes input = new Runes(
'\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}');
print(new String.fromCharCodes(input)); // ♥ 😅 😎 👻 🖖 👍
- Symbols
一个 Symbol object 代表 Dart 程序中声明的操作符或者标识符。
使用 Symbol 字面量来获取标识符的 symbol 对象,也就是在标识符 前面添加一个 # 符号。#radix - Functions方法
- 只有一个表达式,使用=>语法。
- 参数分必须参数和可选参数,可选参数分为可选命名参数和可选位置参数。
- 可选命名参数
定义:
enableFlags({bool bold, bool hidden}) {
}
使用:
enableFlags(bold: true, hidden: false);
- 可选位置参数
定义,参数放在[]中:
String say(String from, String msg, [String device]) {
}
使用:
say('Bob', 'Howdy') // 未使用位置参数
say('Bob', 'Howdy', 'smoke signal') // 使用了位置参数
- 可选参数的默认参数值
默认值只能用编译时常量,否则为null。list和map也可以作为默认参数。
void enableFlags({bool bold = false, bool hidden = false}) {
}
- main函数
程序的入口函数。 - 一等方法
可以把其他方法作为参数调用的方法。
class Test {
printEelement(element) {
print(element);
}
void test() {
var list = [1, 2, 3];
list.forEach(printEelement);
}
}
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
- 匿名方法
也叫lambda或者closure闭包。
var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums'];
list.forEach((i) {
print(list.indexOf(i).toString() + ': ' + i);
});
list.forEach((i) => print(list.indexOf(i).toString() + ': ' + i));
- 函数返回值
如果函数没有指定返回值,则最后一句默认执行return null;
- 操作符
- ~/:返回值为整数的除法
- as 类型转换,is 是否为某个类型 is!是否不是某个类型
- ??= 赋值操作符,如果为null,则赋值,否则不赋值
- 条件表达式:
条件 ? 表达式1 : 表达式2 ,用于赋值操作
表达式1 ?? 表达式2 ,用于判断是否为null操作 - 级联操作符..
querySelector('#button') // Get an object.
..text = 'Confirm' // Use its members.
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
第一个方法 querySelector() 返回了一个 selector 对象。 后面的级联操作符都是调用这个对象的成员, 并忽略每个操作 所返回的值。
无法再返回值为void上执行级联操作
- 其他操作符 ?.,类似swift的可选类型的可选调用,左侧为null,则返回null。
- 流程控制
- 实现了Iterable接口的对象,都可以使用forEach来遍历操作,List和Map都实现了Iterable接口。
candidates元素对象实现了Iterable接口:
candidates.where((c) => c.yearsExperience >= 5)
.forEach((c) => c.interview());
先筛选出大于等于5的元素,再遍历每个元素进行操作。
- for in 循环,支持实现了Iterable接口的对象
var collection = [0, 1, 2];
for (var x in collection) {
print(x);
}
- switch语句
每个case必须有break,空的case语句不用写break,class中必须没有覆写==。
支持标签,continue支持跳转到某个标签。
var command = 'CLOSED';
switch (command) {
case 'CLOSED':
executeClosed();
continue nowClosed;
// Continues executing at the nowClosed label.
nowClosed:
case 'NOW_CLOSED':
// Runs for both CLOSED and NOW_CLOSED.
executeNowClosed();
break;
}
- Assert断言
assert只在检查模式生效,生产模式不执行。
- 异常
- 使用throw抛出异常,也可以抛出任何对象。
- throw是一个表达式,可以用在表达式的场景。
try {
// 异常
} on Exception {
// A specific exception
print("");
} on Exception catch (e) {
// Anything else that is an exception
print('Unknown exception: $e');
} catch (e) {
// No specified type, handles all
print('Something really unknown: $e');
}
- 使用 rethrow 关键字可以 把捕获的异常给 重新抛出。
- finally确保不管是否异常都执行的语句。
- 类Classes
- 单继承
- 使用new 和构造函数来创建对象,如果为常量构造函数,则使用const替代new。
- 使用runtimeType判断实例的类型,返回一个Type类型。
- 每个实例变量为自动生成一个getter。每个none-final变量会自动生成一个setter。
- 构造函数中,当名字冲突的时候才使用this,否则忽略this。
构造函数的简化:
Point(num x, num y) {
this.x = x;
this.y = y;
}
简化为:
Point(this.x, this.y);
- 如果你没有定义构造函数,则会有个默认构造函数。 默认构造函数没有参数,并且会调用超类的 没有参数的构造函数。
- 子类不会继承超类的构造函数。 子类如果没有定义构造函数,则只有一个默认构造函数 (没有名字没有参数)。
- 使用命名构造函数可以为一个类实现多个构造函数。
class Test {
num x;
num y;
// 构造函数简化
Test(this.x, this.y);
// 命名构造函数
Test.fromJson(Map json) {
x = json['x'];
y = json['y'];
}
}
- 超类的构造函数调用顺序
子类提供了有参的构造函数,顺序为:
初始化子类的参数列表 => 调用父类的无名构造函数 => 调用子类的无名构造函数
如果超类没有无名构造函数,则可以在构造函数后使用:来调用,如下: - 初始化参数列表
在构造函数体执行之前除了可以调用超类构造函数之外,还可以 初始化实例参数。 使用逗号分隔初始化表达式。
class Point {
num x;
num y;
Point(this.x, this.y);
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map jsonMap) : x = jsonMap['x'], y = jsonMap['y'] {
print('In Point.fromJson(): ($x, $y)');
}
}
初始化列表非常适合用来设置 final 变量的值:
class Point {
final num x;
final num y;
final num distanceFromOrigin;
Point(x, y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}
- 重定向构造函数
构造函数需要调用其他构造函数,可以在后遭函数定义后,使用冒号调用其他构造函数。
import 'dart:math';
main() {
var p = new Point(2, 3);
}
class Point {
final num x;
final num y;
Point(this.x, this.y);
// 重定向构造函数
Point.fromXAlixs(num x) : this(x, 0);
}
- 常量构造函数
使用const修饰构造函数,其他变量均使用final来修饰。 - 工厂构造函数
使用factory来修饰的构造函数,工厂构造函数无法使用this。
工厂方法用于写单例:
// 用户单例
class UserManager {
// 如果一个函数的构造方法并不总是返回一个新的对象的时候,可以使用factory,
// 比如从缓存中获取一个实例并返回或者返回一个子类型的实例
// 工厂方法构造函数
factory UserManager() => _getInstance();
// instance的getter方法,通过UserManager.instance获取对象
static UserManager get instance => _getInstance();
// 静态变量_instance,存储唯一对象
static UserManager _instance;
// 私有的命名式构造方法,通过它可以实现一个类可以有多个构造函数,
// 子类不能继承internal不是关键字,可定义其他名字
UserManager._internal() {
// 初始化
user = new User(false, "", "", "", "", false, "", false, "", "");
}
// 获取对象
static UserManager _getInstance() {
if (_instance == null) {
// 使用私有的构造方法来创建对象
_instance = new UserManager._internal();
}
return _instance;
}
// 用户对象
User user;
}
- getters/setters
每个实例变量都有一个getter,非final的变量有一个setter,可以自定义getter和setter。
main() {
var rect = Rectangle(3, 9, 5, 7);
print(rect.left);
rect.right = 10;
print(rect.left);
rect.left = 1;
print(rect.right);
}
class Rectangle {
num left;
num top;
num width;
num height;
Rectangle(this.left, this.top,this.width, this.height);
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
num set(num value) => top = value - height;
}
- 抽象函数
抽象函数是只定义函数接口但是没有实现的函数,由子类来 实现该函数。如果用分号来替代函数体则这个函数就是抽象函数。
定义了函数,没有函数的实现,这个函数就是抽象函数。 - 覆写操作符
使用operator
修饰
main() {
var v1 = Vector(2, 5);
var v2 = Vector(3, 4);
print((v1 + v2).x);
print((v1 + v2).y);
}
class Vector {
final int x;
final int y;
const Vector(this.x, this.y);
Vector operator +(Vector v) {
return new Vector(x + v.x, y + v.y);
}
Vector operator -(Vector v) {
return new Vector(x - v.x, y - v.y);
}
}
- 抽象类
使用abstract
修饰class,可以定义抽象函数和非抽象函数(有函数实现)不能被实例化。
main() {
var sub = SubAbstractClass();
sub.test();
sub.test2();
}
abstract class AbstractClass {
void test();
void test2() {
print('test2');
}
}
class SubAbstractClass extends AbstractClass {
@override
void test() {
// TODO: implement test
print('test1');
}
}
- 隐式接口
每个类都隐式的定义了一个包含所有实例成员的接口, 并且这个类实现了这个接口。如果你想 创建类 A 来支持 类 B 的 api,而不想继承 B 的实现, 则类 A 应该实现 B 的接口。
class Person {
final _name;
Person(this._name);
void look() {
print('look');
}
}
class Student implements Person {
@override
get _name => "";
@override
void look() {
// TODO: implement look
}
}
每个类都有一个隐式的接口,供别的类来implements实现。
- 扩展子类
使用extends来创建子类。 - 枚举
enum,枚举无法继承,无法使用mixin,无法implements枚举类型。 - mixin为类添加新功能
mixins在多类继承中重用一个类的代码。使用with关键字。
mixin类的条件:
- 继承于Object而不是继承与其他类
- 没有构造函数
- 不能调用super
main() {
var stu1 = Student('ying');
stu1.look();
stu1.buyBook();
stu1.readBook();
}
class Person {
final _name;
Person(this._name);
void look() {
print('look ' + this._name);
}
}
// mixin类
class BookHandle {
void readBook() {
print('read book');
}
void buyBook() {
print('buy book');
}
}
class Student extends Person with BookHandle {
Student(name) : super(name);
}
- implements/extends/mixin区别:
implements实现多个别的类的接口,不通过继承来实现,每一个类都有一个包含所有实例成员的隐式接口。
extends 创建子类,只能单继承
mixin用于多类集成中重用一个类的代码,解决代码复用问题,不用再去实现同样的功能,用with关键字
mixin类条件:1.继承于Object而不是其他类(不再限定) 2. 没有构造函数 3. 不能调用super(不再限定)
// 普通类
class AClass {
void test() {
}
}
// 抽象类
abstract class BClass {
void testB();
}
// 抽象类
abstract class CClass {
void testC();
}
// 使用implements可以实现多个类的接口
class DClass implements BClass, CClass {
@override
void testB() {
// TODO: implement testB
}
@override
void testC() {
// TODO: implement testC
}
}
// 使用extends单继承
class EClass extends AClass {
}
// 创建mixin
mixin FClass {
final String name = "";
// 需要被实现
void testF();
// mixin实现的功能
void testFF() {
print('testFF');
}
}
// 也是一个mixin,不再限定必须是继承于Object和不再限定不能调用super
abstract class GClass {
void testG();
void testGG() {
print('testGG');
}
}
// 使用minxin
class HClass with FClass, GClass {
@override
void testF() {
// TODO: implement testF
testFF();
}
@override
void testG() {
// TODO: implement testG
testGG();
}
}
- 静态变量和静态函数
- 使用static修饰,静态变量第一次使用的时候才初始化。无法使用this访问。
- 对于通用的或者经常使用的静态函数,考虑 使用顶级方法而不是静态函数。
- 静态函数还可以当做编译时常量使用。例如, 你可以把静态函数当做常量构造函数的参数来使用。
- 泛型
- 用于集合字面量
var names = <String>['ni', 'wo', 'ta'];
var pages = <String, String>{'name': 'tom'};
- 构造函数中使用泛型
var list = new List<String>();
var nameSet = new Set<String>.from(list);
- 用于is判断类型
nameSet is Set<String>
- 限制泛型类型
class Foo<T extends SomeBaseClass> {...}
- 泛型函数
main() {
var test = new Test();
var names = new List<String>();
test.firstTest(names);
}
class Test {
T firstTest<T>(List<T> list) {
if (list.length > 0) {
T tmp = list[0];
return tmp;
} else {
return null;
}
}
}
- 库和可见性
- 一个Dart app就是一个库,使用下划线的标识符只在库里可见。
- 使用库
内置库:import 'dart:io';
包管理器提供的库:import 'package:utils/utils.dart';
其他库:文件系统路径 - 使用as 指定库的前缀,来区分不同的标识符:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// ...
Element element1 = new Element(); // Uses Element from lib1.
lib2.Element element2 = new lib2.Element(); // Uses Element from lib2.
- 导入库的一部分
使用show/hide关键字:
// 只导入foo
import 'package:lib1/lib1.dart' show foo;
// 导入除foo之外的其他
import 'package:lib2/lib2.dart' hide foo;
- 延迟载入库
可以让应用在需要的时候再 加载库,使用场景:
减少app的启动时间
、执行不同的测试
、加载很少使用的功能
使用方法:
先用deferred as
来导入,用的时候,调用loadLibrary()
函数来加载库。
import 'package:deferred/hello.dart' deferred as hello;
greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
loadLibrary()
多次调用只加载一次,async
异步加载库,await
关键字暂停代码执行一直到库加载完成。
延迟加载库的常量在导入的时候是不可用的。 只有当库加载完毕的时候,库中常量才可以使用。
在导入文件的时候无法使用延迟库中的类型。 如果你需要使用类型,则考虑把接口类型移动到另外一个库中, 让两个库都分别导入这个接口库。
Dart 隐含的把loadLibrary()
函数导入到使用deferred as 的命名空间
中。loadLibrary()
方法返回一个 Future。
- 异步支持
一些异步返回Future和Stream对象的方法,设置完就返回了,无需等待操作完成。
- 使用Future对象中的数据
使用async和await
和使用Future API
- 从Stream中获取数据
使用async和一个异步for循环(await for)
和Stream API
要使用await
,方法必须带有async
。
checkVersion() async {
var version = await lookUpVersion();
if (version == expectedVersion) {
// Do something.
} else {
// Do something else.
}
}
- 异步方法
使用async标记函数体的方法,异步方法返回值为Future。
同步方法:
String lookUpVersionSync() => '1.0.0';
异步方法:
Future<String> lookUpVersion() async => '1.0.0';
- await表达式
一个异步方法可以有多个await表达式,默认返回一个Future,如果表达式返回值不是Future,会自动把返回值放到Future中返回。 - await for
await for是作用域Stream的异步for循环。expression表达式的返回值必须是Stream类型的。可使用break和return来控制流程。
await for (variable declaration in expression) {
// Executes each time the stream emits a value.
}
- 可调用的类
如果类实现了call方法,则可以像调用方法一样调用类。
main() {
var test = new Test();
var out = test('name', 1, true);
print(out); // name, 1, true
}
class Test {
call(String str, int num, bool bo) => str + ',' + ' $num, $bo';
}
typedef类型定义
typedef只适用于Function方法类型。
typedef int Compare(Object a, Object b);
注解
使用元数据给你的代码添加其他额外信息。
元数据注解是以 @ 字符开头,后面是一个编译时 常量(例如 deprecated)或者 调用一个常量构造函数。
3个通用的注解:@deprecated
不赞成、@override
重写、 和@proxy
代理。
自定义注解:
todo.dart文件:
library todo;
class todo {
final String who;
final String what;
const todo(this.who, this.what);
}
main.dart文件:
import 'todo.dart';
main() {
}
class Test {
@todo('ying', 'to do something')
void test() {
print('test');
}
}
- 注释
//
、/* */
、///
或/** */