Dart快速入门 语法篇
入门
1 | // 定义个方法。 |
- 注释:
//
或/* ... */
,同其他主流语言 - 类型:num、String、int、bool等
- 字面量:42,’Hello world!’
- 函数:类似
print()
的形式 - 字符串插值
- 入口方法:
main
基本理念
- 所有可以用变量引用的都是对象,每个对象都是一个类的实例,例如数字、方法、null,所有对象都继承Object类
- Dart是强类型语言。但是不强制使用类型标注,因为它可以通过推导得到变量类型。在你明确不希望有类型时,使用
dynamic
关键字表示动态类型 - Dart支持泛型,比如
List<int>
- Dart支持顶级方法
main()
,支持类的静态方法、实例方法,也可以在函数内使用函数 - 类似地,Dart支持全局变量、局部变量和在类中定义的成员变量
- Dart没有public、protected、private的区分,如果标识符以
_
开头,那么该标识符就是私有的 - Dart的变量名只能以下划线和字母开头,后跟字符或数字
- Dart区分语句块和表达式,只有表达式有值。
关键字
分为三类:
- 对于只在特定上下文环境下生效的上下文关键字,可以用作标识符
- 对于内置标识符,为了便于移植JavaScript代码到Dart,这些关键字不可用作类或类型名或import的前缀
- 其他关键字为保留字
变量
1 | var name = 'Dart'; |
根据基本理念,变量都是存储值的引用。使用var
修饰时,变量类型会自动推导;也可以显示声明变量类型,或者使用dynamic
关键字表示变量可能有多种类型。
任何没有初始化的变量默认值都为null。
常量使用final
或const
(实例变量只能用final
)。
1 | final name = 'foo'; |
- final变量只能赋值一次,const变量是编译时常量。
const
除了用来定义不变量,还可以用来创建不变的值,以及定义创建常量的构造函数。在这么用时可以省略构造过程,像上面的baz变量一样
内置类型
- numbers
- strings
- booleans
- lists (也被称之为 arrays)
- maps
- runes (用于在字符串中表示 Unicode 字符)
- symbols
再次重申,Dart中变量都是一个对象,所以你都可以使用构造函数来初始化。
Number
有int
和double
两种类型。提供了原生操作符和abs()
等常用函数,整数和浮点数的字面量初始化类似js。
字符串和数字互转:
1 | // String -> int |
String
Dart的字符串是UTF-16编码的字符序列。可以使用单引号或双引号创建。字符串中用${expr}
的语法使用表达式,**如果表达式是一个标识符,可以省去{}
**,对{}
内的表达式,Dart使用toString()
方法转成字符串使用。
使用'''
或"""
表示多行字符串。使用r''
表示纯字符串。
Boolean
布尔类型有两个字面量值,true
和false
。和JavaScript不同的是,在if
语句等使用bool类型的地方,只有true
被认为是true,其余所有值都是false。这也是为了避免JavaScript中判断true、false时坑爹的地方。
1 | if (1) { |
List
List的字面量写法和JavaScript一样。Dart会做类型推导,在元素类型不一致时报错。你可以使用const语句定义一个不变的List对象。
1 | const list = [1, 2, 3]; |
2.3后,Dart支持...
解构操作符,以及对空列表兼容的...?
。同时支持collection if和collection for语法。
1 | var nav = [ |
Set
2.2版本后支持
一组元素唯一的无序列表。字面量写法类似数学中集合的定义方法。
1 | var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'}; |
也可以使用构造函数的方式创建。
1 | var elements = <String>{}; |
类似List,2.3之后有...
和...?
的语法支持。
Map
表达键值对数据,每个键只出现一次,且可以是任意类型。类似Set,可以使用字面量和构造函数两种方式构造。使用字面量时,Dart会做类型推导。
Map的设置和JavaScript类似,另外类似List,2.3之后有...
和...?
的语法支持。
Rune
Dart用Rune类型表示UTF-32的字符,如emoji等。
Symbol
用来代表Dart中声明的操作符或标识符,可以在标识符前添加#
获取标识符的Symbol对象。
方法
类似JavaScript,Dart中的Function也是对象并具有Function类型。推荐使用显式类型声明方法。
1 | bool isNoble(int atomicNumber) { |
Dart支持箭头函数。
可选参数
可选参数分两种:命名参数、位置参数。
命名参数使用param: value
指定,在调用时使用{param1, param2}
的形式传递参数。支持在参数前添加@required
表示参数必选。
位置参数使用[]
包裹方法参数,使用时不传参数即可。
1 | enableFlags(bold: true, hidden: false); |
定义方法时,可以使用=
定义可选参数的默认值。否则默认值为null。
1 | String say(String from, String msg, |
main函数
每个应用都需要有顶级的main()
函数作为入口,返回值void类型,并且有可选的List<String>
参数(用于解析命令行输入的参数数据)。如
1 | void main() { |
上面的
..
语法为级联调用,表示在一个对象上执行多个操作。
第一公民
类似JavaScript,Dart中Function可以作为参数、返回值、变量、对象使用。同样也有匿名函数可以使用,区别是箭头后是语句块时,不使用箭头,只在之后是表达式时使用箭头。
作用域与闭包
Dart是静态作用域,即变量的作用域在写代码时就确定了,作用域层级即大括号的层级。
类似JavaScript,Dart的闭包意味着方法可以封闭其作用域内的变量。
1 | /// Returns a function that adds [addBy] to the |
返回值
所有函数必须返回一个值,否则默认return null
。
操作符
~/
返回取整截断的商- 使用
==
判断相等性- 会调用左侧对象的
==
方法,和后面的对象对比
- 会调用左侧对象的
- 类型转换:
as
,类型转换,类似typescript中的asis
判断对象是否是指定类型is!
判断对象是否不是指定类型
??=
在value不是null时赋值给变量expr1 ?? expr2
表示如果expr1是非null则返回其值,否则执行expr2并返回..
级联操作符,表示在一个对象上连续调用多个函数以及访问成员变量,可以嵌套?.
和.
类似,但是在左侧操作对象为null时返回null1
2
3
4
5
6
7
8final addressBook = (new AddressBookBuilder()
..name = 'jenny'
..email = 'jenny@example.com'
..phone = (new PhoneNumberBuilder()
..number = '415-555-0100'
..label = 'home')
.build())
.build();
流程控制
- for循环中,Dart会自动捕获当时的index索引值,避免JavaScript中问题。对interable的对象可以使用
forEach()
方法遍历,对List、Set还支持for-in
形式的遍历 switch
中的每个case(除了空case)都必须有break
assert
在检查模式下会被跳过
异常
和JavaScript中的异常类似。
不一样的是,可以使用on
或catch
捕获异常,可以通过rethrow
在其中重新抛出异常。
类
- 构造方式类似ES6中引入JavaScript Class。
- 用成员方式声明的类变量在定义时初始化,也就是在构造函数前
- 可以使用Object的runtimeType属性来判断实例的类型
- 使用const关键字结合构造函数可以构造出不可变的对象实例
构造函数
使用和类名同名的方法作为构造函数(或者使用命名构造函数)。因为把构造函数参数赋值给实例变量的场景太常见了,Dart提供了下面的语法糖。
1 | class Point { |
可以使用命名构造函数实现多个构造函数。
1 | class Point { |
子类不会从父类继承构造函数,在未定义构造函数时,会有一个默认构造函数,这个函数没有参数,且会调起父类的没有参数的构造函数。
在有初始化参数列表(initializer list)的情况下,初始化参数列表在父类构造函数前执行。
- 初始化参数列表
- 父类无参构造函数
- 子类无参构造函数
父类没有无参构造函数时,需要手动调用父类的其他构造函数。
初始化列表
在执行父类构造函数前,可以初始化实例参数。
1 | class Point { |
在冒号右边用逗号隔开初始化表达式。注意:等号右边无法访问this
。
重定向构造函数
重定向构造函数没有代码,在构造函数声明后,用冒号调用其他构造函数
1 | class Point { |
常量构造函数
如果类支持提供状态不变的对象,需要定义一个const
构造函数,且所有类变量都要是final
。
1 | class ImmutablePoint { |
工厂构造函数
当你的构造函数不需要返回新对象,而从其他地方获取时(如缓存),使用工厂构造函数。**工厂构造函数内无法访问this
**。调用时方式和普通构造函数等同。
1 | class Logger { |
方法
类方法可以访问this
,另外对于类对象的每个属性都有隐含的getter和setter(final除外)。也可以显式使用get
和set
定义getter和setter的行为。
1 | class Rectangle { |
- Dart使用
extends
继承,用super
指代父类,用@overide
注解重载操作。 - Dart中有抽象类/抽象方法,设计和使用类似Java的抽象类/抽象方法。如果你希望抽象类可实例化,可以定义一个工厂工造函数。
- 每个类都隐式的定义了一个包含所有实例成员的接口,通过使用
implement
实现若干其他类的API(不包括构造函数) - 可以重载一些操作符,如
+
,-
,[]
,>>
等,实现在特定类上的特定表现
1 | class Vector { |
有意思的是,Dart提供noSuchMethod()
方法,在访问不存在的类实例或方法时被调用。如果没有填写,默认使用Object的同名方法。
1 | @proxy |
枚举
枚举是特殊的类,使用enum
关键字定义。每个枚举值都有index属性的getter
函数,枚举的values
常量可以返回所有枚举值。
1 | enum Color { red, green, blue } |
mixin
Dart中提供了多类继承中重用类代码的mixin,用with
结合mixin类实现,这种类没有构造函数。除非你想像正常类一样使用mixin,否则使用mixin
关键字。
1 | mixin Musical { |
当限制mixin只在特定类中使用时,结合on
让mixin也能调用父类方法。
1 | mixin MusicalPerformer on Musician { |
类变量、函数
使用static
前缀修饰,表示类级别的变量、函数。类变量只在第一次使用时初始化。静态方法无法访问this。
泛型
使用泛型的两个动机:
- 有助于IDE、环境、同事帮你定位问题和代码自动生成
- 减少重复代码
List和Map的泛型定义类似C++风格。
1 | var names = <String>['Seth', 'Kathy', 'Lars']; |
在泛型中使用extends
可以限制泛型的具体类型。在1.21之后,Dart支持泛型函数。
1 | T first<T>(List<T> ts) { |
包管理
使用import
和library
引入和导出模块。_
开头的标识符只在库内部可见。
1 | import 'dart:html'; |
dart:
开头代表内置库,package:
开头代表外部库。外部库使用pub
包管理器管理。
懒加载库
懒加载即在使用时再加载库,如优化app启动时间,加载很可能用不到的功能。
加载时使用deferred as
导入,使用loadLibrary()
方法加载。
1 | import 'package:deferred/hello.dart' deferred as hello; |
异步支持
Dart中返回Future
和Stream
的方法都是异步的,意味着设置好耗时操作(I/O)后就返回。类似ES7中的await
和async
,你也可以像组织同步代码一样组织你的异步代码。
Dart中声明异步方法是在函数名后加入async,这类方法返回一个Future
对象,了解JS中Promise
的同学可以很快理解Future是做什么的。
1 | checkVersion() async { |
在返回值是Stream时,使用await for
的形式接收Stream中的数据。另外别忘了用async
修饰外界函数。
1 | Future main() async { |
生成器函数
惰性生产数据,类似ES6中的function*
。Dart提供两种类型:
- 同步:返回Iterator
- 异步:返回Stream
1 | Iterable<int> naturalsDownFrom(int n) sync* { |
可调用的类
类中实现了call()
方法时,类实例可以当做方法调用。
1 | class WannabeFunction { |
类型别名
类似typescript中的interface定义,Dart可以借助typedef
进行一些更复杂的类型判断。typedef只是类型别名的一种说法。
1 | typedef int Compare(int a, int b); |
元数据
使用元数据给代码添加额外信息,也能便于文档自动生成。
@deprecated
@override
@proxy
你还可以自定义自己的元数据注解:
1 | library todo; |
注释
- 单行,
//
- 多行,
/**/
- 文档注释,
///
开头,或/**
开头,*/
结束
遵守规范的注释风格会有助于文档自动生成。