Dart是Flutter使用的语言,具有面向对象语言的特性。具有热修复功能,可以在虚拟机运行的时候变更代码。
这里推荐一个很详细的Dart教程
https://www.stephenw.cc/2018/04/20/dart-getting-start/
(一)JIT 与 AOT
Dart 是少数同时支持 JIT(Just In Time,即时编译)和 AOT(Ahead of Time,运行前编编译)的语言之一,这使Dart具有运行速度快、执行性能好的特点
那么 JIT 和 AOT 分别是什么呢?
JIT:在运行时即时编译,在开发周期中使用,可以动态下发和执行代码,开发测试效率高,但运行速度和执行性能则会因为运行时即时编译受到影响(代表语言 JS、Python)
AOT:即提前编译,可以生成被直接执行的二进制代码,运行速度快、执行性能表现好,但每次执行前都需要提前编译,开发测试效率低(代表语言 C、C++)
(二)内存分配与垃圾回收
Dart VM 的内存分配策略比较简单,创建对象时只需要在堆上移动指针,内存增长始终是线性的,省去了查找可用内存的过程
在 Dart 中,并发是通过 Isolate 实现的。Isolate 是类似于线程但不共享内存,独立运行的 worker。这样的机制,就可以让 Dart 实现无锁的快速分配
Dart 的垃圾回收,则是采用了多生代算法,新生代在回收内存时采用“半空间”机制,触发垃圾回收时,Dart 会将当前半空间中的“活跃”对象拷贝到备用空间,然后整体释放当前空间的所有内存。回收过程中,Dart 只需要操作少量的“活跃”对象,没有引用的大量“死亡”对象则被忽略,这样的回收机制很适合 Flutter 框架中大量 Widget 销毁重建的场景
(三)单线程模型
支持并发执行线程的高级语言(比如,C++、Java、Objective-C),大都以抢占式的方式切换线程,即:每个线程都会被分配一个固定的时间片来执行,超过了时间片后线程上下文将被抢占后切换。如果这时更在更新线程间的共享资源,抢占后就可能导致数据不同步的问题。解决这个问题的方式,使用锁来保护共享资源,但锁本身又可能会带来性能损耗,甚至出现死锁
Dart 是单线程模型,因为它天然不存在资源竞争和状态同步的问题。这就意味着,一旦某个函数开始执行,就将执行到这个函数结束,而不会被其他 Dart 代码打断。所以 Dart 中并没有线程,只有 Isolate(隔离区)。Isolate之间不会共享内存,就像几个运行在不同进程中的 worker,通过事件循环(Event Looper)在事件队列(Event Queue)上传递消息通信
(四)无需单独的声明式布局语言
Flutter 可以通过 Dart 编译定义,并不需要类似 JSX 或 XML 的声明式布局语言,易于阅读和可视化,开发中更不需要可视化界面构建起,因为热重载可以让我们立即在手机上看到运行效果
Dart的变量和类型
转自 链接
定义变量的语法很简单,比如:
var name = ‘Bob’; int line = 0; // 定义 int类型 String foo = ‘Bar’; // 定义 String 类型 List counts = [1, 2, 3]; // 定义 List 类型 |
在 Dart中,变量都是引用类型,也就是说所有的变量都是对象,所以 Dart是一门完全面向对象的语言。
Dart 是类型安全的,所以当你使用 var
关键字定义变量时,本质其实就是具体类型的引用。比如上文代码其实就是一个 String
类型对象的引用,这个对象的内容是 ‘Bob’
未初始化变量
未初始化的变量它的值都是 null,在 Dart中这一点很叫人放心。
int lineCount; assert(lineCount == null); |
类型可选
当你使用 var
定义变量时,表示类型是编译器推断决定的,当然你也可以用静态类型去定义变量。
用静态类型去定义变量的好处是你可以更清楚地跟编译器表达你的意图,这样编译器和编辑器就能使用这些类型向你提供 warning或者代码补全这些功能。
不过在 Dart2中,类型不再是可选的。意思是当你使用 var
定义 name变量时,这个类型便会约束在 String类型,如果赋值其他类型便会出错。如果你不想约束一个变量的类型,那么可以使用 Object
类型定义或者 dynamic
关键字定义。
final 和 const
如果你想定义不可变的变量,那么在定义前加上 final
或者 const
关键字。
final nickname = ‘ccc’; const int count = 3; const List list = const []; |
const
表示变量在编译期间即能确定值,如果这个变量是 class
内的,就用 static const
标记。使用const
标记变量时,你需要在声明的时候就给定其值。
final
有些不太一样,用它标记的变量可以在运行时确定,比如:
var x = 100; // 运行时变量,可以是任意其他值 var y = 30; // 运行时变量,可以是任意其他值 final res = x / y; |
final
其实就是在运行时声明一个不可变的引用,这个引用一旦确定就不可再变。
类型
Dart 内置了如下类型:
- Number
- String
- Boolean
- List
- Map
- Rune
- Symbol
在不引入其他库的情况下,你可以使用这些类型声明变量。
Number
Dart 的 number类型其实只有两种:int 和 double
int 的范围是 -2^53 ~ 2^53
double 是符合 IEEE 754标准的 64位双精度的浮点数。
int
和 double
都是 num
的子类型。除了常见的运算符外,你还能使用内置的 abs()
、ceil()
、floor()
等方法。如果还有其他运算方法的需求,你可以引入 dart:math
库来尝试用一下。
String
Dart的 String其实由 UTF-16的代码串组成。和 JavaScript一样,你既能使用单引号也能使用双引号来写字符串的字面量
var s1 = ‘Single quotes work well for string literals.’; var s2 = “Double quotes work just as well.”; var s3 = ‘It\’s easy to escape the string delimiter.’; var s4 = “It’s even easier to use the other delimiter.”; |
你还能在字符串中嵌入变量,除了 String类型的变量外,Dart对字符串中嵌入的对象会默认调用它的 toString()
方法:
var s = ‘string interpolation’; var s1 = ‘this is uppercased string: ${s.toUpperCase()}’ |
字符串的拼接直接使用 +
运算符,这个就不多提了。
值得一提的是声明多行字符串时,你可以用三个单引号或三个双引号,这有点像 Python:
var s1 = ”’ You can create multi-line strings like this one. ”’; var s2 = “””This is also a multi-line string.”””; |
Rune
提到 String,不得不提一下 Rune,Rune其实就是 Dart中的 UTF-32的字符串。
因为 Unicode给每个单词、字符或者符号都定义了特殊的数字值,所以要拿 String去表示 32位的 Unicode会比较困难。
通常地,一个 Unicode码的形式是 \uXXXX,这个 XXXX表示 4位长度的十六进制数。比如字符(♥)的 Unicode码是 \u2665
,但如果要表示的码超过或者少于 4位,就要用花括号括起来,比如 ???? 就是 \u{1f600}
.
Rune其实就是帮助 String来表示 32位 Unicode的。比如下例:
main() { var clapping = ‘\u{1f44f}’; print(clapping); print(clapping.codeUnits); print(clapping.runes.toList()); Runes input = new Runes( ‘\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}’); print(new String.fromCharCodes(input)); } |
输出是:
???? [55357, 56399] [128079] ♥ ???? ???? ???? ???? ???? |
很清楚可以看到,String的 codeUnits
函数只能返回 16位的码,但Rune
就可以表示 32位。
Boolean
Dart 的 bool 类型只有两个值: true 和 false
和其他语言不一样的地方是,当执行判断时,Dart中只有 true才会判定真,其他任何值都为 false
var name = ‘Bob’; if (name) { // 在 Dart中不会执行到这儿 print(‘You have a name!’); } |
List
你也可以管 List叫 Array,但 Dart中数组就叫 List了。
var list = [1, 2, 3]; |
Dart的 List其实是有类型的,比如上述代码的类型就是 List<int>
, 所以你往这里面添加元素必须是 int类型的。
和其他语言一样,数组索引也从 0开始。同样地,你还可以像 JavaScript那样迭代数组:
list.forEach((item) { print(‘index: ${list.indexOf(item)}, item:${item}’); }); |
Map
Map类型在其他语言中也叫做字典,即 key-value的集合。在 Dart中声明 Map也很简单:
var gifts = { // Key: Value ‘first’: ‘partridge’, ‘second’: ‘turtledoves’, ‘fifth’: ‘golden rings’ }; // 或者 var gifts = new Map(); gifts[‘first’] = ‘partridge’; gifts[‘second’] = ‘turtledoves’; gifts[‘fifth’] = ‘golden rings’; |
同样地,Map也有类型,上述代码即 Map<String, String>类型。但很多时候我们并不会按照既定的泛型使用 Map,如果你仔细阅读了上文,你会发现解决方法也很简单。
关于 Map的操作方法你也可以参考 Collections in Dart
Symbol
symbo是 Dart中比较特殊的一种类型,你一般不会用到它,但如果你需要获取标识符的字面量时,这个作用就无法估量了。
var name = ‘a’; Symbol s = #name; |
所以一般情况下,没什么作用,不过这个东西的高级用法我们以后会提到。
The End
通过变量和类型的了解可以认识到:
- 在 Dart中,所有变量都是对象,而所有对象都是类的实例,null也不例外。所有的类都从
Object
继承而来 - 尽量不要偷懒,为变量指定类型,这样编译器和编辑器都能更好地帮助你写的代码。