Dart语言其实早在2011年 Google就发布了该语言。Dart的诞生最早是要解决 JavaScript 存在的、在语言本质上无法改进的缺陷。Dart在最初的定位是运行在浏览器中的脚本语言。
Dart都有哪些特性呢?
- Dart同时支持JIT和AOT的语言之一,这使得在开发调试时效率得到很大提升。
- Dart VM的内存分配策略比较简单,创建对象时只需要在堆上移动指针,内存增长始终是线性的,省去了查找可用内存的过程。在 Dart 中,并发是通过 Isolate 实现的。Isolate 是类似于线程但不共享内存,独立运行的 worker。这样的机制,就可以让 Dart 实现无锁的快速分配。Dart 的垃圾回收,则是采用了多生代算法。新生代在回收内存时采用“半空间”机制,触发垃圾回收时,Dart 会将当前半空间中的“活跃”对象拷贝到备用空间,然后整体释放当前空间的所有内存。回收过程中,Dart 只需要操作少量的“活跃”对象,没有引用的大量“死亡”对象则被忽略,这样的回收机制很适合 Flutter 框架中大量 Widget 销毁重建的场景.
- Dart中没有线程,只有Isolate。Isolate之间不会共享内存,通过事件循环在事件队列上传递消息通信。相比支持并发线程的高级语言,就能够避免由于抢占资源加锁带来的性能损耗甚至是死锁的严重问题。 单线程模型后面会详细介绍。
Dart的特性介绍完了,我们来看看Dart语言的相关基础
从“hello world”开始,体验下Dart
printInteger(int a) {
print('Hello world, this is $a.');
}
main() {
var number = 2019;
printInteger(number);
}
Dart 跟大多数语言一样,要求以main函数作为执行的入口的,运行结果:
Hello world, this is 2019.
Dart的类型与变量
在 Dart 中,我们可以用 var 或者具体的类型来声明一个变量。当使用 var 定义变量时,表示类型是交由编译器推断决定的,当然你也可以用静态类型去定义变量,更清楚地跟编译器表达你的意图,这样编辑器和编译器就能使用这些静态类型,向你提供代码补全或编译警告的提示了。在默认情况下,未初始化的变量的值都是 null,因此我们不用担心无法判定一个传递过来的、未定义变量到底是 undefined,还是烫烫烫而写一堆冗长的判断语句了。Dart 是类型安全的语言,并且所有类型都是对象类型,都继承自顶层类型 Object,因此一切变量的值都是类的实例(即对象),甚至数字、布尔值、函数和 null 也都是继承自 Object 的对象。也就是说 如果我们在声明一个变量没有赋值的情况下该变量都是null。数据类型的使用声明跟其他语言基本一致,这里就不赘述了。需要说明的是dart内的num类型只有两种子类:即 64 位 int 和符合 IEEE 754 标准的 64 位 double。前者代表整数类型,而后者则是浮点数的抽象。
常量定义
如果你想定义不可变的变量,则需要在定义变量前加上 final 或 const 关键字:const,表示变量在编译期间即能确定的值;
final 则不太一样,用它定义的变量可以在运行时确定值,而一旦确定后就不可再变。
对于流程控制语法:如 if-else、for、while、do-while、break/continue、switch-case、assert跟其他语言类似,可以到dart官网或者使用过程中慢慢学习。
函数
在 Dart 中,所有类型都是对象类型,函数也是对象,它的类型叫作 Function。这意味着函数也可以被定义为变量,甚至可以被定义为参数传递给另一个函数。
下面这段代码示例我们来感受下:
bool isZero(int number) { //判断是否为0 由于函数方法体只有一行,我们也可以写成
// bool isZero(int number) => number==0;
return number == 0;
}
void printInfo(int number,Function check) { //用check函数来判断整数是否为0
print("$number is Zero: ${check(number)}");
}
Function f = isZero;
int x = 10;
int y = 0;
printInfo(x,f); // 输出 10 is Zero: false
printInfo(y,f); // 输出 0 is Zero: true
在这里我们定义了printInfo函数,和isZero函数,我们将isZero函数传给printInfo函数,完成判断整数是否为0的打印。
在面向对象的开发中 都提供了函数的重载,那么Dart中是如何实现函数重载的呢?其实Dart 认为重载会导致混乱,因此从设计之初就不支持重载,而是提供了可选命名参数和可选参数。具体的方式:
1、给参数增加{},以 paramName: value 的方式指定调用参数,也就是可选命名参数;
2、给参数增加[],则意味着这些参数是可以忽略的,也就是可选参数。
在使用这两种方式定义函数时,我们还可以在参数未传递时设置默认值。
在 Flutter 中会大量用到可选命名参数的方式,通过个例子感受下:
void main() {
printInfoOne(name: "张三", sex: "男"); // 张三,男
printInfoTwo("张三", "男"); // 张三,男
printInfoOne(name: "张三"); // 张三,女
printInfoTwo("张三"); // 张三,女
}
void printInfoOne({String name, String sex = "女"}) => print("$name,$sex");
void printInfoTwo(String name, [String sex = "女"]) => print("$name,$sex");
类
类的声明跟其他语言一样使用class关键字。需要说明的是在dart中没有public、protected、private 这些关键字,我们只要在声明变量与方法时,在前面加上“”即可作为 private 方法使用。如果不加“”,则默认为 public。不过,“_”的限制范围并不是类访问级别的,而是库访问级别。
在类的构造函数语法糖进行初始化
String name;
String sex;
class Person(this.name,this.sex);
继承
在面向对象的编程语言中,将其他类的变量与方法纳入本类中进行复用的方式一般有两种:继承父类和接口实现。当然,在 Dart 也不例外。这里对继承就不过多阐述了。
下面通过个例子来说明下Dart中的继承与接口的差别
class Point {
num x = 0, y = 0;
void printInfo() => print('($x,$y)');
}
//PointOne继承自Point
class PointOne extends Point{
num z = 0;
@override
void printInfo() => print('($x,$y,$z)'); //覆写了printInfo实现
}
//PointTwo是对Point的接口实现
class PointTwo implements Point {
num x = 0, y = 0; //成员变量需要重新声明
void printInfo() => print('($x,$y)'); //成员函数需要重新声明实现
}
var xxx = PointOne();
xxx
..x = 1
..y = 2
..z = 3; //级联运算符,等同于xxx.x=1; xxx.y=2;xxx.z=3;
xxx.printInfo(); //输出(1,2,3)
var yyy = PointTwo();
yyy
..x = 1
..y = 2; //级联运算符,等同于yyy.x=1; yyy.y=2;
yyy.printInfo(); //输出(1,2)
print (yyy is Point); //true
print(yyy is PointTwo); //true
可以看出,子类 PointTwo 采用接口实现的方式,仅仅是获取到了父类 Point 的一个“空壳子”,只能从语义层面当成接口 Point 来用,但并不能复用 Point 的原有实现。那么,我们是否能够找到方法去复用 Point 的对应方法实现呢?
除了继承和接口实现之外,Dart 还提供了另一种机制来实现类的复用,即“混入”(Mixin)。混入鼓励代码重用,可以被视为具有实现方法的接口。这样一来,不仅可以解决 Dart 缺少对多重继承的支持问题,还能够避免由于多重继承可能导致的继承歧义,无法明确调用的方法是哪个父类的方法。
要使用混入,只需要 with 关键字即可
class PointTwo with Point {
}
var yyy = PointTwo();
print (yyy is Point); //true
print(yyy is PointTwo); //true
Mixin会在后面着重分析,为什么混入的方式可以避免继承歧义。
运算符
1、?. 运算符:假设 Point 类有 printInfo() 方法,p 是 Point 的一个可能为 null 的实例。那么,p 调用成员方法的安全代码,可以简化为 p?.printInfo() ,表示 p 为 null 的时候跳过,避免抛出异常。
2、??= 运算符:如果 a 为 null,则给 a 赋值 value,否则跳过。这种用默认值兜底的赋值语句在 Dart 中我们可以用 a ??= value 表示。
3、?? 运算符:如果 a 不为 null,返回 a 的值,否则返回 b。在 Java 或者 C++ 中,我们需要通过三元表达式 (a != null)? a : b 来实现这种情况。而在 Dart 中,这类代码可以简化为 a ?? b
在 Dart 中,一切都是对象,就连运算符也是对象成员函数的一部分。
我们也可以对运算符做覆写:
class Point {
num x, y;
Point(this.x, this.y);
// 自定义相加运算符,实现向量相加
Point operator +(Point v) => Point(x + v.x, y + v.y);
// 覆写相等运算符,判断向量相等
bool operator == (dynamic v) => x == v.x && y == v.y;
}
final x = Point(3, 3);
final y = Point(2, 2);
final z = Point(1, 1);
print(x == (y + z)); // 输出true
operator 是 Dart 的关键字,与运算符一起使用,表示一个类成员运算符函数。在理解时,我们应该把 operator 和运算符作为整体,看作是一个成员函数名。