Dart基础知识
Dart 变量声明
在Dart中变量声明的关键词是var或者直接标明变量类型,例如:
1 | var name = 'Bob'; |
而final和const主要是针对常量的命名,差别主要体现在不同的赋值时机:
- final是在运行时进行赋值
- const则是编译时进行赋值
比如在实际开发过程中,基于一个widget举例,我们可以使用final去修饰这个widget的一些属性,代码如下:
1 | class MyWidget extends StatelessWidget { |
当我们在使用这个widget的时候,就可以给这个widget传递title和color的值,这样就可以保证这个widget的属性是不可变的。
而const只能放在顶层或者静态变量中,比如:
1 | const a = 1; |
这样的代码是可以通过编译的,因为const是在编译时进行赋值的,所以这样的代码是可以通过编译的。
那么反面案例则是:
1 | const d = DateTime.now(); |
d的取值只有在运行时才能具体得知,所以d是无法通过编译的。
除此之外,在class内部也可以使用const去修饰一些属性,比如:
1 | class MyWidget extends StatelessWidget { |
比如上述这样的静态属性就可以使用const来修饰。
在类的构造函数中,也可以使用const去修饰构造函数,比如:
1 | class Point { |
这样的构造函数就是一个常量的构造函数,在通过这个构造函数去创建多个实例的时候,多个实例占据的实际上是同一段内存空间,这样可以减少内存的占用。
此处我还需要补充一个很小的点,在开发flutter页面时,也可以用const去修饰不可变的组件,比如:
1 | const Text("Text"); |
这样可以减少不必要的widget重建,提高性能。
类型推导与显式声明
在Dart中,即使不标明变量类型,Dart也可以根据变量的值来推导出变量的类型,比如:
1 | var name = 'Bob'; |
这与JavaScript中的let和const有点类似,但是Dart的类型推导更加严格,比如:
1 | var name = 'Bob'; |
而对比到JavaScript中,这样的代码是可以正常运行的。
此外,Dart还有一个特殊的类型dynamic,这个类型是在运行时才能确定的,比如:
1 | dynamic name = 'Bob'; |
但是我的开发习惯是尽量地避免使用dynamic类型,因为这样会导致在项目逐渐变得庞大后,变量的类型难以确定。
空安全
Dart中的空安全是指在编译时就能确定变量是否为空,这样可以避兼容性问题,比如:
1 | String name = null; // 这里会报错,因为name是String类型,不能为null |
在Dart中,?和!操作符是用来处理空安全的,?表示这个变量可以为空,!表示这个变量不为空,比如:
1 | String? name = null; |
这样的空安全机制可以在编译时就能发现潜在的问题,提高代码的健壮性。
异步编程
Dart的异步编程主要是通过Future和Stream来实现的,这里先着重说一下Future。
Future是一个和JavaScript的Promise很类似的概念,它表示一个异步操作的结果,比如:
1 | Future<String> fetchUserOrder() { |
1 | function fetchUserOrder(): Promise<string> { |
这两段代码是非常类似的,都是表示一个异步操作的结果,只不过Dart中的Future是一个类,而JavaScript中的Promise是一个构造函数。可以说,
只要明白了Promise的用法,那么Future也就不难理解了。
async, await, Future.delayed
Dart中的async和await与JavaScript中的async和await是非常类似的,都是用来处理异步操作的,比如:
1 | Future<void> printOrderMessage() async { |
1 | async function printOrderMessage() { |
整体的写法也是大同小异的,这里再额外补充一个无栈协程的概念,是我看到的一篇v2ex上的文章,很不错,可以参考一下。
Future.delayed是一个延迟执行的方法,和JavaScript中的setTimeout是类似的,比如:
1 | Future.delayed(Duration(seconds: 2), () { |
1 | setTimeout(() => { |
这两段代码的作用是一样的,都是延迟2秒后执行一个函数。
Dart 异步执行时的 Isolate vs JavaScript 的 Web Worker
由于我在实际开发中并没有使用到多线程的场景,所以这里先标记为TBD
作用域与闭包
Dart中的作用域和JavaScript中的作用域是非常类似的,比如:
1 | void main() { |
1 | function main() { |
这两段代码的作用是一样的,都是在函数内部定义一个变量,然后在内部函数中使用这个变量。最终输出的结果都是Bob,这就说明Dart的作用域是词法作用域。
wiki对词法作用域的定义是这样的:
静态作用域又叫做词法作用域,采用词法作用域的变量叫词法变量。词法变量有一个在编译时静态确定的作用域。
词法变量的作用域可以是一个函数或一段代码,该变量在这段代码区域内可见(visibility);在这段区域以外该变量不可见(或无法访问)。词法作用域里,取变量的值时,会检查函数定义时的文本环境,捕捉函数定义时对该变量的绑定。
关于闭包,Dart中的闭包和JavaScript中的闭包是非常类似的,比如:
1 | Function makeCounter() { |
Dart 闭包的核心特点:
- 可以访问外部作用域的变量,即使外部作用域已结束
- 保留对外部变量的引用,而不是拷贝
- 每次调用闭包时,变量的状态会被保留
私有成员(_前缀)
Dart中类的私有成员是通过_前缀来实现的,比如:
1 | class MyClass { |
当我们在类外部访问这个私有成员时,会报错,比如:
1 | void main() { |
类与对象
Dart中的类和对象与JavaScript中的类和对象是非常类似的,比如:
1 | class Person { |
extends 和 implements
Dart中的继承和实现与JavaScript中的继承和实现是非常类似的,比如:
1 | class Person { |
implements的用法也是类似的,比如:
1 | // 普通类 |
extends和implements的区别在于,extends是继承一个类(继承了父类的所有非私有成员),而implements是实现一个接口(包括了普通类以及抽象类)。
Dart 的 mixin
Dart中的mixin是一种不能被实例化的类,它可以被其他类使用with关键字来继承,比如:
1 | mixin Logger { |
可以看出mixin可以给类添加一些额外的功能,这在实际开发中是非常有用的,尤其是flutter默认就给提供了很多的mixin,例如SingleTickerProviderStateMixin等。
静态方法与实例方法
Dart中的静态方法和实例方法与JavaScript中的静态方法和实例方法是非常类似的,比如:
1 | class MyClass { |
类里的静态方法是通过static关键字修饰的,实例方法则没有修饰符,并且子类继承父类时,静态方法是不会被继承的。
库与包管理
Dart中的库和包管理与JavaScript中的模块化系统是非常类似的,比如:
1 | import 'dart:math'; // Dart内置库 |
包管理是通过pubspec.yaml文件来管理的,类似与JavaScript中的package.json,这里是一份比较典型的pubspec.yaml:
1 | name: play_flutter |
这里的dependencies是用来管理项目的依赖,dev_dependencies是用来管理项目的开发依赖,flutter是用来配置flutter项目的一些信息。