弱类型(var、object 和 dynamic)
相对 JavaScript 而言,Dart 也存在弱类型(可以使用 var、object 和 dynamic 来声明),不过在这方面为了避免弱类型导致的客户端(App)Crash 的异常,Dart 还是对弱类型加强了校验。
var 数据类型声明,第一次赋值时,将其数据类型绑定。下面代码使用 var 声明了一个弱类型 t,并赋值 String 类型 123,而接下来又对 t 进行其他类型的赋值。
var t = '123';
t = 123;
这样的代码在 Dart 编译前就会报错,因为 t 在一次 var 赋值时就已经被绑定为 String 类型了,再进行赋值 Number 类型时就会报错。
Assign value to new local variable
object
可以进行任何赋值,没有约束,这一点类似 JavaScript 中的 var 关键词赋值。在编译期,object 会对数据调用做一定的判断,并且报错。例如,声明时为 String 类型,但是在调用 length 时,编译期就会报错。如果数据来自接口层,则很容易导致运行时报错。因此这个要尽量减少使用,避免运行时报错导致客户端(App)Crash 的异常。
dynamic 也是动态的数据类型,但如果数据类型调用异常,则只会在运行时报错,这点是非常危险的,因此在使用 dynamic 时要非常慎重。
基础运算符
两种语言的基础运算符基本都一致。由于 Dart 是强数据类型,因此在 Dart 中没有 “=== ”的运算符。在 Dart 中有一些类型测试运算符,与 JavaScript 中的类型转换和 typeof 有点相似。
这里也介绍一些 Dart 中比较简洁的写法:
-
?? 运算符,比如,t??'test' 是 t!= null ? t : 'test' 的缩写;
-
级联操作,允许对同一对象或者同一函数进行一系列操作,例如下面代码的 testObj 对象中有三个方法 add()、delete() 和 show(),应用级联操作可以依次进行调用。
testObj.add('t')
..delete('d')
..show()
函数
两者区别不大。箭头函数、函数闭包、匿名函数、高阶函数、参数可选等基本上都一样。在 Dart 中由于是强类型,因此在声明函数的时候可以增加一个返回类型,这点在 TypeScript 中的用法是一致的,对于前端开发人员来说,没有太多的差异点。
类
类的概念在各种语言上大部分都是一致的,但在用法上可能存在差异,这里着重介绍一下 Dart 比较特殊的一些用法。
命名构造函数
Dart 支持一个函数有多个构造函数,并且在实例化的时候可以选择不同的构造函数。
下面的代码声明了一个 Dog 类,类中有一个 color 变量属性和两个构造函数。red 构造函数设置 Dog 类的 color 属性为 red,black 构造函数设置 Dog 类的 color 属性为 black。最后在 main 函数中分别用两个构造函数创建两个实例,并分别打印实例的 color 属性。
class Dog {
String color;
Dog.red(){
this.color = 'red';
}
Dog.black(){
this.color = 'black';
}
}
void main(List<String> args) {
Dog redDog = new Dog.red();
print(redDog.color);
Dog blackDog = new Dog.black();
print(blackDog.color);
}
运行代码后输出了两种颜色,即 red 和 black。就代码而言,我们可以应用同一个类不同的构造函数实现类不同场景下的实例化。
访问控制
默认情况下都是 public,如果需要设置为私有属性,则在方法或者属性前使用 “_”。
抽象类和泛型类
抽象类和其他语言的抽象类概念一样,这里在 JavaScript 中没有这种概念,因此这里稍微提及一下,主要是实现一个类被用于其他子类继承,抽象类是无法实例化的。
下面的代码使用关键词 abstract 声明了一个有攻击性的武器抽象类,包含一个攻击函数和一个伤害力获取函数,Gun 和 BowAndArrow 都是继承抽象类,并需要实现抽象类中的方法。
abstract class AggressiveArms {
attack();
hurt();
}
class Gun extends AggressiveArms {
attack() {
print("造成100点伤害");
}
hurt() {
print("可以造成100点伤害");
}
}
class BowAndArrow extends AggressiveArms {
attack() {
print("造成20点伤害");
}
hurt() {
print("可以造成20点伤害");
}
}
泛型类,主要在不确定返回数据结构时使用,这点与 TypeScript 中的泛型概念一样。
在下面的代码中,我们不确定数组中存储的类型是 int 还是 string,又或者是 bool,这时候可以使用泛型 来表示。在使用泛型类的时候可以将设定为自己需要的类型,比如下面的 string 调用和 int 调用。
class Array<T> {
List _list = new List<T>();
Array();
void add<T>(T value) {
this._list.add(value);
}
get value{
return this._list;
}
}
void main(List<String> args) {
Array arr = new Array<String>();
arr.add('aa');
arr.add('bb');
print(arr.value);
Array arr2 = new Array<int>();
arr2.add(1);
arr2.add(2);
print(arr2.value);
}
库与调用
Dart 库管理
Dart 和 JavaScript 一样,有一个库管理资源(pub.dev)。你可以在这里搜索找到你想要的一些库,接下来只要在 Dart 的配置文件 pubspec.yaml 中增加该库即可。这点类似于在 JavaScript 的 package.json 中增加声明一样,同样也有 dependencies 和 dev_dependencies。
增加类似的数据配置,如下代码:
dependencies:
cupertino_icons: ^0.1.2
dio: ^3.0.4
image_test_utils: ^1.0.0
dev_dependencies:
flutter_test:
sdk: flutter
开发 Dart 库
Dart 也支持开发者自己开发一些库,并且发布到 pub.dev 上,这点基本上和 npm 管理一致,这里我只介绍 pub.dev 库的基本格式。
dart_string_manip
├── example
| └── main.dart
├── lib
| ├── dart_string_manip.dart
| └── src
| ├── classes.dart
| └── functions.dart
├── .gitignore
├── .packages
├── LICENSE
├── README.md
├── pubspec.lock
└── pubspec.yaml
对于前端开发人员来说,这个结构和我们所看到的 npm 模块很相似,pubspec 和 package 很相似,核心是 lib 中的库名对应的库文件 .dart,该文件是一个 dart 类。类的概念上面已经介绍过了,将私有方法使用 "_" 保护,其他就可以被引用该库的模块调用,如果是自身库的一些实现逻辑,可以放在 src 中。
开发完成该库以后,如果需要发布到 pub.dev,则可以参照官网的说明,按步骤进行即可。
Dart 调用库
这里引入库的方式也与 ES6 的 import 语法很相似。先看看下面的一个例子,其目的是引入 pages 下的 homepage.dart 模块。
import 'package:startup_namer/pages/homepage.dart';
在上面的例子中,import 为关键词,package 为协议,可以使用 http 的方式,不过最好使用本地 package 方式,避免性能受影响。接下来的 startup_namer 为库名或者说是该项目名,pages 为 lib 下的一个文件夹,homepage.dart 则为具体需要引入的库文件名。
当然这里也可以使用相对路径的方式,不过建议使用 package 的方式,以保持整个项目代码的一致性,因为对于第三方模块则必须使用 package 的方式。
总结
首先介绍了 Dart 基础数据类型、基础运算符、类以及库与调用