概念(core concepts)
- dart:一切皆对象,所有对象都是继承自Object类。所有变量声明语句的默认值为 “null”。
- 类型声明:定义变量时需要声明变量的类型,并且不能在后续程序中给变量赋予其它类型的值。
- 泛型:可以指定容器内容可装载的数据类型,例如:List
只能添加 int 类型的值,如需容纳所有类型可以使用 List 泛型。 - 全局函数:全局函数和普通函数一样,但可被任何模块访问。Dart 需要 main全局函数 作为程序运行的入口,main函数支static, async await。
- 全局变量:Dart 同样支持全局变量,定义与 main 前面,可被内部其余模块直接访问。
- 变量命名:以 “” 或 字母开头,后面可跟数字。以 “” 开头的变量意味着局部可访问。
- 打印函数:print 打印输出结果,类似JS的 console.log。
- 注释:“//” 单行注释,“/../” 多行注释,“///” 文档注释
- 异常:警告级别不会中断程序运行,错误级别将中断程序运行。
内建变量(Variable)
- numbers 数值类型
int:整数类型,取值范围:-2^63 ~ 2^63 - 1,不支持小数点。
double:浮点数,64 位长度的浮点类型,小数点前面占16位。
num:int 和 double 的基类,支持基本运算符“+”,“-”,“*”,“/”。int 类型还支持位运算符 “>>”,“<<”,“|”,“&” 。
常用API:abs,ceil,floor 。 - strings
String: utf-16编码字符串,可用单引号或双引号字面量,内部支持 {} 执行表达式。字符串可直接多个字符串拼接,多行字符串可使用 '''str''' 。常用API:contains,startsWith,endsWith,indexOf,substring,split,toUpperCase,toLowerCase,trim,replaceAll。
StringBuffer:字符串缓存区,通过 write、writeAll 写入数据,仅当调用 toString 时才生成字符串。
RegExp:正则表达式,通过 hasMatch,allMatches 校验字符串,实例化时可使用Runes字符串( '\d+' => r'\d+' )。字符串查询相关API也支持正则匹配。 - booleans
bool:布尔类型变量只支持 true、false 值。条件分支 “if-else” 只支持bool值判断,如果非bool值,则抛出异常。 - lists
List: 列表,泛型变量 List,泛型字面量 [1,2]。指定泛型后,后续添加的元素均不能为其它类型变量、字面量。 - sets
Set:不允许重复元素的集合,泛型变量 Set,泛型字面量 {1,2}。目前不支持泛型字面量,只能使用泛型变量进行元素类型限制。常用API:add,addAll,contains,containsAll。 - maps
Map:kv字典,泛型变量 Map,泛型字面量 {'str': 2}。key和value可为任何类型对象,key不可以重复。常用API: keys,values,containsKey,containsValue,remove,update。 - runes
Runes:utf-32编码字符串,两字节固定编码,用于支持表情图编码字符集。字符编码 - symbols
Symbol:不可变更标识符,字面量 #symbol,为编译时常量,即使压缩丑化代码也不会被变更。相同字符的标识符相等。
函数(Function)
- Dart 是面向对象编程语言,函数也属于 Object 类型。
- 函数声明:支持 声明 或 不声明 函数返回值类型,匿名函数由声明的变量类型指定函数返回类型。如果函数体仅包含单行表达式,可简写为:bool isTest(int number) => number === 10;
- 可选参数:可选参数跟在必填参数后面,可指定其 位置 或 名称。如:
// optional named parameters function
bool namedParams({ bool test }) => test;
namedParams(test: true);
// optional positional parameters function
bool positionalParams(bool test, [ bool optional ]) => test && optional;
positionalParams(true);
// set default value of optional params(default to null)
void defaultValue({ bool value = true }) => value;
- main函数:main函数作为应用的入口函数,返回void,并通过 List
获取命令行参数。 - 匿名函数:([ [type] params [, ...] ]) { codeBlock };
- 作用域:函数内部的环境变量只能被函数内部访问,函数外部无法访问。
- 闭包:函数引用了外部作用域,并在将来有可能会被调用。
类(Classes)
- 类定义:类定义只能在 main 函数外部,main 内部不识别 class 关键词。
- 成员变量:用于描述类的属性,类中定义的 成员变量 可被每个实例对象访问。成员变量 支持 getter 和 setter,系统默认会为 成员变量 创建 getter,non-final变量 还会默认创建 setter 。定义 成员变量 时可进行赋值操作,该操作将会先于任何构造函数执行,若不进行赋值操作,默认为 null。( final变量初始化必须先于构造函数 )
class Person {
final String name; // “_” 表示私有变量,但实际运行中可被外部访问。
Person(String name): this.name = name {} // 简写 Person(this.name);
}
- 构造函数:
默认构造函数:与类同名的构造函数,类定义若不包含默认构造函数,将使用不带参数的系统默认构造函数。如果为继承类,将无参调起调起父类构造函数。
命名构造函数:在类名后面追加一个标识符,一般用于支持多种类型传参实例化。
转移构造函数:仅对参数进行简单处理,然后通过其余构造函数完成对象实例化。
class Person{
String name;
// 默认构造函数,变量初始化在所有构造函数之前完成
Person(this.name);
// 命名构造函数,通过 fromJson 说明类支持 Json 参数进行实例化
Person.fromJson(Map data): name = data['name'] {}
// 转移构造函数,在姓名前面添加 ID,然后通过默认构造函数完成实例化
Person.nameWithID(String name): this('ID_${name}');
}
常量构造函数:若实例对象不会有变更操作,可把类实例化作为编译时常量,多个常量对象之间相互等价。含有常量构造函数的类,所有 成员变量 必须为 final 变量。(不能通过 this 访问实例对象)
class ImmutablePerson {
final String name; // final 变量
const ImmutablePerson(this.name); // 返回常量值,不包含函数体
static final ImmutablePerson man = const ImmutablePerson('man'); // 静态属性
}
工厂构造函数:默认构造函数前加上 “factory” 关键字,实例化时可指定返回对象,可能为类的实例化对象、或 其它对象。(不能通过 this 访问实例对象)
class Test {
final String name;
static Map _cache = {};
factory Test(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
_cache[name] = Test._internal(name); // 构造实例并缓存
return _cache[name];
}
}
Test._internal(this.name); // 最终构造函数
}
调用父类非默认构造函数:若父类没有使用默认构造函数(匿名、无参),则子类必须手动调用父类的构造函数,并传递相应的参数。
class Person {
final String name;
Person.fromName(this.name); // 类定义了命名构造函数,默认构造函数失效
}
class Employee extends Person {
final String job;
// 父类构造函数的调用,必须位于初始化列表的最后面。
Employee.fromNameAndJob(name, this.job): super.fromName(name) {}
}
- 成员方法:用于描述类的行为、功能,类中定义的 成员方法 可被每个实例调用,但不同实例的方法因其所处作用域环境不同而不相等 assert(obj1.func !== obj2.func)。同时,Dart 成员函数还支持 getter,setter,和 abstract methods 。
abstract class Rectangle {
num top, left, width, height;
Rectangle(this.top, this.left, this.width, this.height);
// getter 方法,作为实例对象的属性
num get right => left + width;
num get bottom => top + height;
// setter 方法,实例属性 right 支持赋值操作
set right(num value) => left = value - width;
set bottom (num value) => top = value - height;
// 成员方法,可被实例直接调用(覆盖父类 Object 成员函数)
String toString() {
return '($top, $left, $bottom, $right)';
}
// 抽象方法,仅抽象类方可定义抽象方法,抽象函数的具体实现由其它类实现
String abstractFunction();
}
class Test extends Rectangle {
Test(top, left, width, height): super(top, left, width, height) {}
String abstractFunction() {
return 'This is a Abstract Function Test';
}
}
- 抽象类:自身无法进行实例化,仅对类进行抽象的描述,通常是对 成员方法 进行抽象描述,并由 派生类 完成方法的具体实现。
abstract class AbstractContainer {
void abstractMethod(); // 抽象方法没有函数体
}
- 接口:描述一个类定义所必须要实现的 成员变量 和 成员方法,每个类定义隐式的创建了相应的 接口,其它类通过 implements 引入接口定义,并需要提供接口定义的 变量 和 方法 的具体实现(支持多接口引入)
class Person {
final _name; // 接口暴露的 属性
Person(this._name); // 构造函数不属于 接口类 的定义
String greet(String who) => 'Hello, $who. I am $_name'; // 接口暴露的 方法
}
class Impostor implements Person {
get _name => 'test'; // 实现 Person._name 定义
// 实现 Person.greet 定义,函数的参数和返回值必须与接口一致
String greet(String who) => 'Hi $who. Do you know who I am?';
}
- 继承:继承父类所有的 成员变量 和 成员方法,并可在 派生类 中被重写,也支持 noSuchMethod、操作符 的重写。Dart 支持单继承,但可以使用 Mixin 方式实现类定义代码的复用。
class Test {
final String memVariable; // 成员变量
Test(this.memVariable); // 构造函数,将先于 派生类 构造函数的调用
String memMethod() => memVariable; // 成员方法
}
class TestExt extends Test {
TestExt(String variable): super(variable) {} // 父类构造函数先被调用
@override
String memMethod() => 'override'; // 重写父类的 memMethod 方法
// 重写 noSuchMethod
@override // 访问类不存在的属性或方法时的处理,
void noSuchMethod(Invocation invocation) {
print('non-existent member: ${invocation.memberName}');
}
// 重写 “+” 操作符定义
TestExt operator +(TestExt other) => TestExt(this.memVariable + other.memVariable)
}
- 获取实例运行时类型:obj.runtimeType即可获取 obj 属于什么类的实例对象。
特殊操作符
- 类型检测
is:A is B, A是否继承自,或就是 B 对象(is!)。
as:A as B,把A当B来调用,A 必须为继承自 B,或实现 B 接口定义的类。 - null 处理
“?.”: 如果对象不包含指定属性,返回 null。
“??”: 如果右边值为 null,返回左边值。
“??=”: 如果变量当前为 null,则进行重新赋值。 - 链式调用(级联符)
querySelector('#confirm') // Get an object.
..text = 'Confirm' // Use its' members.
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
异常
- 异常 Exception
抛出异常如果不被捕获并进行处理,将导致程序终止运行。可继承Exception实现自定义异常。 - 抛出异常 throw
支持多种类型异常抛出,包括字符串类型、对象字面量等等。 - 重新抛出异常 rethrow
直接把当前的异常重新抛出,不需要重新定义异常。 - 捕获异常 catch
捕获异常,并对不同类型异常进行相应处理,阻止程序因为异常的冒泡而终止。 - 最终运行语块 finally
无论程序是否产生异常、异常是否被捕获处理,都会进入 finally 程序。
try {
breedMoreLlamas();
} on OutOfLlamasException {
// A specific exception,and omit "e" param
buyMoreLlamas();
} on Exception catch (e) {
// Anything else that is an exception
print('Unknown exception: $e');
} catch (e,s) { // "s" is stack trace
// No specified type, handles all
print('Something really unknown: $e');
}
特殊声明
- types declaration
var:定义变量时不指定变量类型,若初始化时进行了赋值,则隐式声明变量类型,后续不能赋予其它类型的值;若初始化时不进行赋值,则后续可以赋予任何类型的值。
dynamic: 指定变量为动态类型,后续可以赋予任何类型的值。
Object:指定变量为 Object 类型,由于dart中所有值都是对象,而且对象都继承自Object,因此 Object 类型的变量可以容纳任何类型的值。 - final & const (omit types declaration)
final:定义变量时必须初始化,并且不允许被重复赋值,但如果以 List 等复杂类型初始化,可通过 List API 改变其内部的值。类定义中使用 final 定义属性值时,属性的初始化必须在构造函数运行前完成。
final x;
Point.fromJson(Map json) : x = json['x'] { ... }
const:编译时常量,必须以 “字面量”,“常量”,或 它们的运算表达式 进行赋值,并且不可变更,即使使用 List 等复杂类型,也不允许调用其 API 变更内部的值。类定义中使用 static const 定义静态常量。
仓库管理: package Manager
pub是dart项目中依赖包管理器,类似node项目的npm。pub通过分析pubspec.yaml分析项目的依赖,并默认从 pub.dartlang.org 中拉去相应的依赖到本地。但是,如果没有翻墙,拉去会一直失败。