Dart语法基础
Dart语法基础
Dart语言简介
在Dart官方网站上,对于Dart的描述如下:
Developers at Google and elsewhere use Dart to create high-quality, mission-critical apps for iOS, Android, and the web. With features aimed at client-side development, Dart is a great fit for both mobile and web apps.
Google和其他地方的一些开发者使用Dart语言为Android、iOS和web构建高质量,关键任务的应用程序,针对客户端开发的特点,Dart非常适合移动和Web应用程序。
Dart是Google推出的一门编程语言,最初是希望取代Javascript运行在浏览器端,后来慢慢发展成可以开发Android、iOS和Web端App的一门高质量的编程语言,目前Dart的版本是Dart2,官网是:www.dartlang.org/
更加详细的可以参考
- 英文官方文档 www.dartlang.org/
- 中文官方文档 http://dart.goodev.org/guides/language
- 在线联系 https://dartpad.dartlang.org/
核心概念
Dart语言博采众长,在我们学习Dart时候需要记住以下核心概念
- 一切都是对象,一切对象都是class的实例,哪怕是数字类型、方法甚至null都是对象,所有的对象都是继承自
Object - Dart是强类型语言,但变量类型是可选的因为Dart可以自动推断变量类型
- Dart支持范型,
List<int>表示一个整型的数据列表,List<dynamic>则是一个对象的列表,其中可以装任意对象 - Dart支持顶层方法(如
main方法),也支持类方法或对象方法,同时你也可以在方法内部创建方法 - Dart支持顶层变量,也支持类变量或对象变量
- 跟Java不同的是,Dart没有
publicprotectedprivate等关键字,如果某个变量以下划线(_)开头,代表这个变量在库中是私有的
变量
声明变量的方式
可以明确指定某个变量的类型,如int bool String,也可以用var或 dynamic来声明一个变量,Dart会自动推断其数据类型。
1 | main() { |
final和const
final修饰的变量是不可改变的,而const修饰的表示一个常量,声明普通变量可以使用var
内置数据类型
Dart有如下几种内建的数据类型:
- numbers
- strings
- booleans
- lists(或者是arrays)
- maps
- runes(UTF-32字符集的字符)
- symbols
String
- 检测两个 String 的内容是否一样事,我们使用 == 进行比较;如果要测试两个对象是否是同一个对象(indentity test),使用 identical 函数
- 进行拼接
- ${} 字符串内表达式
- 三个单引号或者双引号可以创建多行字符串对象
List
http://dart.goodev.org/guides/libraries/library-tour#collections
初始化
1 | // Use a List constructor. |
核心api
- add
- addAll
- length
- indexOf
- sort
Map
map是一个关联键和值的对象。键和值都可以是任何类型的对象。每个键只出现一次,但是您可以多次使用相同的值。Dart对map的支持是通过map字面量和map类型来提供的。
初始化
1 | var gifts = { |
核心api
添加
1
2var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds'; // Add a key-value pair获取map对象
1
2var gifts = {'first': 'partridge'};
assert(gifts['first'] == 'partridge');
如果查找的key不存在则返回null
- length 获取map中键值对的数目
- containsKey
- putIfAbsent()
但是只有该 key 在 map 中不存在的时候才设置这个值,否则 key 的值保持不变
Set
初始化
Set 是一个无序集合,里面不能保护重复的数据。 由于是无序的,所以无法通过索引来从 set 中获取数据
1 | var ingredients = new Set(); |
核心api
contains()和containsAll()来判断 set 中是否包含 一个或者多个对象
常用集合函数
List 和 Set 实现了 Iterable ,虽然 Map 没有实现 Iterable,但是 Map 的 keys 和 values 属性实现了 Iterable
isEmpty
forEach()函数可以遍历集合数据,在 Map 上使用forEach()的时候,方法需要能 接收两个参数(key 和 value):1
2
3
4
5
6
7
8
9var teas = ['green', 'black', 'chamomile', 'earl grey'];
teas.forEach((tea) => print('I drink $tea'));
//map
hawaiianBeaches.forEach((k, v) {
print('I want to visit $k and swim at $v');
// I want to visit Oahu and swim at
// [Waikiki, Kailua, Waimanalo], etc.
});可以使用
map().toList()或者map().toSet()来 强制立刻执行 map 的方法:
函数
返回值
Dart是一个面向对象的编程语言,所以即使是函数也是一个对象,也有一种类型Function,这就意味着函数可以赋值给某个变量或者作为参数传给另外的函数。虽然Dart推荐你给函数加上返回值,但是__不加返回值的函数同样可以正常工作__,另外你还可以用=>代替return语句
1 |
|
所有的函数都有返回值,如果没有指定return语句,那么该函数的返回值为null。
命名参数、位置参数、参数默认值
- 命名参数
使用花括号将函数的参数括起来就是定义了命名参数
1 | sayHello({String name}) { |
可以以{type paramName}或者{paramName: type}两种方式声明参数,而调用命名参数时,需要以funcName(paramName: paramValue)的形式调用。
可以使用@required注解来标识一个命名参数,这代表该参数是必须的,你不传则会报错
1 | const Scrollbar({Key key, @required Widget child}) |
- 位置参数
使用中括号[]括起来的参数是函数的位置参数,代表该参数可传可不传,位置参数只能放在函数的参数列表的最后面
1 | sayHello(String name, int age, [String hobby]) { // 位置参数可以有多个,比如[String a, int b] |
- 参数默认值
可以为命名参数或者位置参数设置默认值
1 | // 命名参数的默认值 |
函数作为一等方法对象
函数作为参数传给另一个函数
1 | printNum(int a) { |
或者将函数赋值给某个变量
1 | printNum(int a) { |
匿名函数
创建没有名字的方法,称之为 匿名方法,有时候也被称为 lambda 或者 closure 闭包。 你可以把匿名方法赋值给一个变量, 然后你可以使用这个方法,比如添加到集合或者从集合中删除
匿名函数类似于
Java中的接口,往往在某个函数的参数为函数时使用到。词法闭包
一个 闭包 是一个方法对象,不管该对象在何处被调用, 该对象都可以访问其作用域内 的变量。
下面的示例中,makeAdder() 捕获到了变量 addBy。 不管你在那里执行 makeAdder() 所返回的函数,都可以使用 addBy 参数。
1 | Function makeAdder(num addBy) { |
操作符
| 描述 | 操作符 |
|---|---|
| unary postfix | expr++ expr-- () [] . ?. |
| unary prefix | -expr !expr ~expr ++expr --expr |
| multiplicative | * / % ~/ |
| additive | + - |
| shift | << >> |
| bitwise AND | & |
| bitwise XOR | ^ |
| bitwise OR | ` |
| relational and type test | >= > <= < as is is! |
| equality | == != |
| logical AND | && |
| logical OR | ` |
| if null | ?? |
| conditional | expr1 ? expr2 : expr3 |
| cascade | .. |
| assignment | = *= /= ~/= %= += -= <<= >>= &= ^= ` |
类型判定操作符
as、 is、 和 is! 操作符是在运行时判定对象 类型的操作符:
| 操作符 | 解释 |
|---|---|
as |
类型转换 |
is |
如果对象是指定的类型返回 True |
is! |
如果对象是指定的类型返回 False |
赋值操作符
使用 = 操作符来赋值。 但是还有一个 ??= 操作符用来指定 值为 null 的变量的值。
1 | a = value; // 给 a 变量赋值 |
条件表达式
Dart 有两个特殊的操作符可以用来替代 if-else 语句:
condition?expr1:expr2
如果 condition 是 true,执行 expr1 (并返回执行的结果); 否则执行 expr2 并返回其结果。
如果你需要基于布尔表达式 的值来赋值, 考虑使用?:。1
var finalStatus = m.isFinal ? 'final' : 'not final';
expr1??expr2
如果 expr1 是 non-null,返回其值; 否则执行 expr2 并返回其结果。
如果布尔表达式是测试值是否为 null, 考虑使用??。1
String toString() => msg ?? super.toString();
级联操作符
级联操作符 (..) 可以在同一个对象上 连续调用多个函数以及访问成员变量。 使用级联操作符可以避免创建 临时变量, 并且写出来的代码看起来 更加流畅:
1 | querySelector('#button') // Get an object. |
级联调用也可以嵌套:
1 | final addressBook = (new AddressBookBuilder() |
在方法上使用级联需要十分小心,无法再void上使用级联操作符
1 | // Does not work |
其他操作符
| Operator | Name | Meaning |
|---|---|---|
() |
使用方法 | 代表调用一个方法 |
[] |
访问 List | 访问 list 中特定位置的元素 |
. |
访问 Member | 访问元素,例如 foo.bar 代表访问 foo 的 bar 成员 |
?. |
条件成员访问 | 和 . 类似,但是左边的操作对象不能为 null,例如 foo?.bar 如果 foo 为 null 则返回 null,否则返回 bar 成员 |
控制流程
if / else switch for /while try / catch语句跟Java中都类似,try / catch语句可能稍有不同
try catch
捕获异常可以避免异常继续传递(你重新抛出rethrow异常除外)。 捕获异常给你一个处理 该异常的机会:
1 | try { |
对于可以抛出多种类型异常的代码,你可以指定 多个捕获语句。每个语句分别对应一个异常类型, 如果捕获语句没有指定异常类型,则 该可以捕获任何异常类型:
1 | try { |
使用 on 来指定异常类型,使用 catch 来 捕获异常对象。
使用 rethrow 关键字可以 把捕获的异常给 重新抛出。
1 | final foo = ''; |
类
类的定义与构造方法
Dart中的类没有访问控制,所以你不需要用private, protected, public等修饰成员变量或成员函数
1 | class Person { |
上面的Person类中有3个成员变量,一个构造方法和一个成员方法,看起来比较奇怪的是Person的构造方法,里面传入的3个参数都是this.xxx,而且没有大括号{}包裹的方法体,这种语法是Dart比较独特而简洁的构造方法声明方式,它等同于下面的代码:
1 | Person(String name, int age, String gender) { |
由于Dart中的类没有访问控制权限,所以你可以直接用obj.var的方式访问一个对象的成员变量。
命名构造函数
使用命名构造函数可以为一个类实现多个构造函数, 或者使用命名构造函数来更清晰的表明你的意图:
1 | class Point { |
调用超类构造函数
子类的构造函数会自动调用超类的 无名无参数的默认构造函数。 超类的构造函数在子类构造函数体开始执行的位置调用。 如果提供了一个 initializer list(初始化参数列表) ,则初始化参数列表在超类构造函数执行之前执行。 下面是构造函数执行顺序:
- initializer list(初始化参数列表)
- superclass’s no-arg constructor(超类的无名构造函数)
- main class’s no-arg constructor(主类的无名构造函数)
如果超类没有无名无参数构造函数, 则你需要手工的调用超类的其他构造函数。 在构造函数参数后使用冒号 (:) 可以调用 超类构造函数。Dart中使用extends关键字做类的继承,如果一个类只有命名的构造方法,在继承时需要注意,如下代码:
1 | class Human { |
重定向构造函数
有时候一个构造函数会调动类中的其他构造函数。 一个重定向构造函数是没有代码的,在构造函数声明后,使用 冒号调用其他构造函数。
1 | class Point { |
抽象类和抽象方法
使用abstract修饰一个类,则这个类是抽象类,
抽象类中可以有抽象方法和非抽象方法,抽象方法没有方法体,需要子类去实现,如下代码:
1 | abstract class Doer { |
枚举类
枚举类型通常称之为 enumerations 或者 enums, 是一种特殊的类,用来表现一个固定 数目的常量。
1 | enum Color { |
- 枚举类型中的每个值都有一个
indexgetter 函数, 该函数返回该值在枚举类型定义中的位置(从 0 开始)。 例如,第一个枚举值的位置为 0, 第二个为 1. - 枚举的
values常量可以返回 所有的枚举值。
枚举类型具有如下的限制:
- 无法继承枚举类型、无法使用 mix in、无法实现一个枚举类型
- 无法显示的初始化一个枚举类型
Mixins 为类添加新的功能
Mixins 是一种在多类继承中重用 一个类代码的方法。使用 with 关键字后面为一个或者多个 mixin 名字来使用 mixin。 下面是示例显示了如何使用 mixin:
1 | class A { |
静态成员变量 和 静态成员方法
1 | // 类的静态成员变量和静态成员方法 |
泛型
http://dart.goodev.org/guides/language/language-tour#generics%E6%B3%9B%E5%9E%8B
使用泛型可以减少冗余的代码, 泛型可以在多种类型之间定义同一个实现,在Dart中Dart 的泛型类型是固化的,在运行时有也 可以判断具体的类型。例如在运行时(甚至是成产模式) 也可以检测集合里面的对象类型:
1 | var names = new List<String>(); |
这个跟Java中是不同的 Java 中的泛型信息是编译时的,泛型信息在运行时是不纯在的。 在 Java 中你可以测试一个对象是否为 List, 但是你无法测试一个对象是否为 List
限制型泛型
使用 extends 可以实现这个功能
1 | // T must be SomeBaseClass or one of its descendants. |
泛型函数
一开始,泛型只能在 Dart 类中使用。 新的语法也支持在函数和方法上使用泛型了。
1 | T first<T>(List<T> ts) { |
这里的 first (<T>) 泛型可以在如下地方使用 参数 T :
- 函数的返回值类型 (
T). - 参数的类型 (
List<T>). - 局部变量的类型 (
T tmp).
库的可见性
普通引用
1
2
3
4
5
6
7// demo.dart
import './util.dart';
main() {
print(add(1, 2));
}as关键字 设置前缀
1
2
3
4
5
6
7
8import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// Uses Element from lib1.
Element element1 = Element();
// Uses Element from lib2.
lib2.Element element2 = lib2.Element();show hide 关键字导入包的部分功能
1
2
3
4
5// 只导入foo
import 'package:lib1/lib1.dart' show foo;
// 导入除了foo的所有其他部分
import 'package:lib2/lib2.dart' hide foo;deferred as 懒加载
1
import 'package:greetings/hello.dart' deferred as hello;
异步
Dart提供了类似ES7中的async await等异步操作,这种异步操作在Flutter开发中会经常遇到,比如网络或其他IO操作,文件选择等都需要用到异步的知识。async和await往往是成对出现的,如果一个方法中有耗时的操作,你需要将这个方法设置成async,并给其中的耗时操作加上await关键字,如果这个方法有返回值,你需要将返回值塞到Future中并返回,如下代码所示:
1 | Future checkVersion() async { |
更多请见 https://segmentfault.com/a/1190000014396421
可调用类
如果 Dart 类实现了 call() 函数则 可以当做方法来调用。
1 | class WannabeFunction { |
元数据
使用元数据给你的代码添加其他额外信息。 元数据注解是以 @ 字符开头,后面是一个编译时 常量(例如 deprecated)或者 调用一个常量构造函数。
有三个注解所有的 Dart 代码都可以使用: @deprecated、 @override、 和 @proxy
你还可以定义自己的元数据注解。 下面的示例定义了一个带有两个参数的 @todo 注解:
1 | library todo; |
使用
1 | import 'todo.dart'; |
元数据可以在 library、 class、 typedef、 type parameter、 constructor、 factory、 function、 field、 parameter、或者 variable 声明之前使用,也可以在 import 或者 export 指令之前使用。 使用反射可以在运行时获取元数据 信息。







