Google及全球的其他开发者,使用Dart开发了一些列高质量、关键的IOS、Android和web应用。Dart非常适合移动和web应用开发。
概述
高效
Dart语法清晰简洁,工具简单而强大。输入检测可帮助您尽早识别细微错误。Dart拥有久经考验的核心库(core liberaries)和一个已经拥有数以千计的packages生态系统。
快速
Dart 提供提前编译优化,以在移动设备和web上实现可预测的高性能和快速启动。
可移植
Dart可以编译成ARM和c86代码,因此Dart移动应用程序可以在iOS,Android及更高版本上实现本地运行。对于web应用程序,Dart可以转换为JavaScript。
易学
Dart是面向对象的编程语言,语法风格对于许多现有的开发人员来说都很熟悉。如果您已经了解C++,C#或Java,那么Dart也就是分分钟的事情。
响应式
Dart可以便捷的进行相应式编程。由于快速对象分配和垃圾收集器的实现,对于管理短期对象(比如UI小部件),Dart更加高效。Dart可以通过Future和Stream的特性和API实现异步编程。
准备
环境准备
体验Dart语言和它的核心API,DartPad够用了。或者你也可以通过安装教程安装Sdk。
Pub包管理
使用pub工具管理Dart包,安装DartSDK时会自动安装pub。有些Dart技术不是全部支持pub命令的(如Flutter)。
IDE和编辑器
- AndroidStudio
- Visual Studio Code
- IntelliJ IDEA
解决方案
应用类型 | SDK | 工具信息 |
---|---|---|
移动端 | 安装Flutter | Flutter工具 |
Web | 安装DartSdk | DartWeb端工具 |
脚本或服务器 | 安装DartSDK | 服务端开发工具 |
重要概念
- 任何保存在变量中的都是一个对象,并且所有的对象都是对应一个类的实例。无论是数字,函数和null都是对象。所有对象继承自Object类。
- Dart是强类型语言,同时 也可以推断类型,所以类型注释是可选的。如果不需要任何类型使用 dynamic。
- Dart支持泛型,如 List&聊天;int>或者List<dynamic>
- Dart支持顶级函数(例如 main()),静态函数、实例函数,以及支持函数内部创建函数(嵌套或局部函数)。
- Dart支持顶级变量,静态变量,实例变量(又称作字段、属性)
- 没有 public,protected,private。以“_”开头的则属于私有,
- 标识符以字母或下划线(_)开头,后跟任意字母和数字组合。
- Dart语法中包含 表达式 和 语句 。
- Dart提示有两种类型:警告和错误。警告表明代码可能无法正常工作,但不会阻止程序的执行。错误可能是编译时错误或者运行时错误。编译时错误会阻止代码的执行;运行时错误会导致代码执行过程中引发一场。
变量
var\final\const
普通类型声明使用var 或者对应类型如(String)。
final\const 声明的变量 不可以被修改。const变量是隐士final类型的,第一次使用时被初始化。
1 | var name="lisi"; |
内建数据类型
- Number
- String
- Boolean
- List
- Map
- Set
- Rune(用于在字符串中标识Unicode字符)
- Symbool
这些类型都可以被初始化为字面量。例如,‘this is a string’ 是一个字符串的字面量,true 是一个布尔的字面量。
因为 在Dart中所有变量都是一个对象,所以变量可以使用构造函数进行初始化。
函数
函数也是对象,类型是Function。可赋值给变量或作为参数传递给其他函数。
箭头语法(=>)
{return expr;}可以简写为=>expr;
1 | int m(int a){return a+1;} |
可选参数
可选参数可以是命名参数或者位置参数,但一个参数只能选择其中一种方式修饰。
命名可选参数
调用函数时,可以使用指定命名参数paramsName:value.例如
method1(bold:true,hidden:false);
定义函数是,{param1,param2,……}来命名参数
void method1({boolean bold,boolean hidden});
位置可选参数
1 | String say(String from,String msg,[String device]){ |
默认参数值
在定义方法的时候,可以使用 = 来定义可选参数的默认值,未定义 默认null。
1 | void m1(boolean bold=false,bool hidden=true){...} |
主意:在Dart旧版本参数只支持:符号,所以采用(:)进行赋值,新版本已经弃用(😃。
main函数
任何应用必须有一个顶级的main()函数,作为应用服务的入口。返回void,参数:可选List
1 | void main(){ |
函数是一等对象
一个函数可以作为另一个函数的参数。例如:
1 | void printE(var a){ |
一个函数赋值给一个变量。例如:
1 | var m1=(msg)=>'${msg.toUpperCase}'; |
匿名函数
1 | ([[Type] param1[,……]]){ |
作用域
Dart是一门词法作用域的编程语言,就意味着变量作用域是固定的,简单说变量的作用域在编写代码的时候就已经确定了。花括号内的是变量可见的作用域。
闭包
返回值
所有函数都有返回值,如果没有明确指定返回值,函数体会隐式添加return null;语句。
1 | foo(){} |
运算符
下表是Dart定义的运算符。多数运算符可以被重载。
描述 | 操作符 |
---|---|
后缀 | expr++ expr-- () [] . ?. |
前缀 | -expr !expr ~expr ++expr --expr |
算数运算符 | * / % ~/ + - |
shift | << >> >>> |
按位与 | & |
按位异或 | ^ |
按位或 | l |
类型判断 | as is is! |
关系 | == != >= > <= |
逻辑运算 | && ll |
if null | ?? |
条件表达式 | expr?expr2:expr3,expr1??expr2 |
级联操作 | … |
赋值运算 | = *= 、= += -= ……= etc. |
控制流程语句
- if and esle
- for loops
- while and do-while loops
- break and continue
- switch and case
- assert
异常
Dart 代码可以抛出、捕获异常。和Java 不同,Dart中的所有异常都是非检查异常。方法不会声明它们抛出的异常,也不要求捕获任何异常。
- Exception Error 2中类型及其子类。此外 程序还可以抛出任何非null对象,不仅限于Exception、error对象。
1 | throw FormatException('xxx'); |
提示:规范建议只抛出 Exception、Error 异常。
类
Dart 是一种基于类和mixin继承机制的面向对象语言。每个对象都是一个类的实例,所有类都继承于 Object。基于Mixin继承意味着每个类都只有一个超类,一个类中的代码可以在其他多个继承类中重复使用。
成员变量
- 使用(.)来引用实例对象、方法。
- 使用(?.)可以避免因为左边的对象可能为null,导致的异常。
1 | var p=Point(2,2); |
构造函数
通过构造函数创建对象。构造函数名字是ClassName 或者 ClassName.identifier。例如
1 | var p1=Point(2,2); |
版本提示:在Dart2中 new关键字可选。
获取对象类型
使用对象的runttimeType属性,返回Type对象。
1 | print('The type of a is ${a.runtimeType}'); |
实例变量
未初始化实例变量 都为null。
所有实例变量生产隐式getter方法。非final的实例变量同样生成隐式setter方法(不是通过get方法调用哦,是通过(.))。
1 | class Point{ |
调用父类非默认构造函数
默认情况,子类的构造函数会在函数开始时 调用父类的默认构造函数(匿名、无参数)。。
- initializer list(初始化参数列表)
- superclass’s no-arg constructor(父类无名构造函数)
- main class’s no-arg constructor (主类无名构造函数)
如果父类没有匿名无参构造函数,需要手动调用其它构造函数。在当前构造函数冒号(:)之后,函数体之前。
1 | Employee.fromJson(Map data):super.fromJson(data){ |
重定向构造函数
1 | class Point{ |
常量构造函数
如果该类生成的对象是固定不变的,那么可以把这些对象定义为编译时常量。为此需要定义一个const构造函数,并声明所有实例变量 final。
1 | class ImmutablePoint{ |
工厂构造函数
当执行构造函数并不需要总是创建这个类的一个新实例时,则使用factory关键字。
1 | class Logger{ |
提示:工厂构造函数无法访问this。
方法
方法是为对象提供行为的函数。
实例方法
对象的实例放阿飞可以使用this 和实例变量。
1 | class Point{ |
Getter、Setter
Getter、Setter是用于对象属性读和写的特殊方法。回想之前的例子,每个实例变量都有一个隐式的getter,通常情况下还有一个setter。
抽象方法
1 | abstract class Doer{ |
隐式接口
重写类成员
@override注解指出想要重写的成员
1 | class A extends B{ |
重写运算符
这是一个神奇的功能,
举例重写 +、-操作符
1 | class Vector { |
重写noSuchMethod()
当代码尝试使用不存在的方法或实例变量时,通过重写noSuchMethod方法,来实现检测和应对处理。
枚举类型
使用枚举
枚举中每个值都有一个index getter方法,从0开始。
1 | enum Color{red,green,blue} |
switch语句 如果不处理所有的枚举,会收到警告;
限制:
- 不能被子类化,混合或实现
- 不能被显示实例化
为 类添加功能:Mixin
Mixin 是复用类代码的一种途径,复用的类可以在不同层级,之间可以不存在继承关系。
通过with后面跟一个或多个混入的名称,来使用Mixin,例如
1 | class Musician extends Performer with Musical{...} |
指定某些类型可以使用Mixin,使用on来指定可以使用Mixin的父类类型:
1 | mixin MusicalPerformer on Musician{ |
版本提示:mixin关键字在Dart2.1中支持。之前使用 抽象类代替。
类变量和方法
使用 static关键字实现类的范围的变量和方法。
静态变量
静态变量只到他们被使用的时候才会初始化。
静态方法
静态方法(类方法)不能在实例上使用,因此他们不能访问this。
对于常见或广泛使用的工具和函数,应该考虑使用顶级函数而不是静态方法。
泛型
在API文档中 你会发现基础数组类型List的实际类型是List<E>通常情况下,使用一个字母代表类型参数,例如 E,T,S,K,V等。
- 正确指定泛型类型可以提高代码质量
- 使用泛型可以减少重复的代码。
集合字面量
List,Set和Map字面量也是可以参数化的。参数化字面量和之前的字面量定义类似,对于List或Set只需要在声明语句前加<type>前缀,对于Map只需要在声明语句前加<keyType>,valueType>前缀,示例:
1 | var names=<String>['a','b','c']; |
泛型类型的构造函数
1 | var nameSet=Set<String>.from(names); |
运行时中的泛型集合
Dart中的泛型类型是固化的,也就是说它们在运行时是携带者类型信息的。例如,在运行时检测集合的类型:
1 | var names=List<String>(); |
相反,Java中的泛型会被擦除,也就是说在运行时泛型类型参数的信息时不存在的。在Java中,可以测试对象是否为List类型,但无法测试它们是否为List<String>.
限制泛型类型
使用泛型类型的时候,可以使用extends实现参数类型的限制。
1 | class Foo<T extends SomeBaseClass>{ |
泛型函数
最初,Dart的泛型只能用于类。新语法_泛型方法_,允许在方法和函数上使用此类参数:
1 | T first<T>(List<T> ts){ |
库的和可见性
import 和library 指令可以用来创建一个模块化的,可共享的代码库。库不仅提供了API,而且对代码起到了封装的作用:以下划线(_)开头的标识仅在库内可见。每个Dart应用程序都是一个库,虽然没有使用library指令。库可以通过包来分发。这里使用pub管理。
使用库
1 | import 'dart:html'; |
import 参数只需要一个指向库的URI。对于内置库,URI拥有自己特殊的dart:方案。对于其他库,使用系统文件路径或者package:方案。package:方案制定由包管理器(如pub工具)提供的库。
1 | import 'package:test/test.dart'; |
提示:URI代表同意资源标识符,URL(统一资源定位符)是一种常见的URI。
指定库的前缀
1 | import 'package:lib1/lib1.dart' ; |
导入库的一部分
1 | import 'package:lib1/lib1.dart' show foo; |
延迟加载库
Deferred loading(也称lazy loading)可以让应用在需要的时候再加载库。
- 减少app启动时间
- 执行A/B测试
- 加载很少使用的功能,例如可选的屏幕和对话。
1 | import 'pacakge:greetings/hello.dart' deferred as hello; |
实现库
- 如何组织库的源文件。
- 如何使用export命令。
- 合适使用part命令。
- 合适使用library命令。
异步
Dart库包含许多返回Future或Stream对象的函数。这些函数时在设置完耗时任务后,就立刻返回了,不会等待耗时任务完成。使用async和await关键字实现异步编程。可以让你向写同步代码一样实现异步操作。
Future
获取Future执行完成的结果:
- 使用async 和 await。
- 使用 FutureAPI
1 | await lookUpVersion(); |
虽然异步函数可能会执行耗时的操作,但它不会等待这些操作。相反,异步函数只有在遇到第一个await表达式时才会执行。也就是说,它返回一个Future对象,仅在await表达式完成后才恢复执行。
声明异步函数
函数体被async标识符标记的函数,即是一个 异步函数。例如:
1 | String lookUpVersion()=>'1.0.0'; |
Stream
获取Stream数据:
- 使用 async 和一个异步循环(await for).
- 使用Stream API
1 | await for (varOrType identifier in expression){ |
上面表达式返回的值必须是Stream类型。流程如下:
- 等待,直到流发出一个值。
- 执行for循环体,将变量设置为该出发的值
- 重复1和2,直到关闭流。
要在应用程序的函数中使用异步for循环,函数必须标记async:
1 | Future method() async{ |
可调用类
通过实现类的call()方法,函数接收三个字符串参数,函数体将三个字符串拼接。
1 | class M{ |
Isolates
所有Dart代码都在隔离区(isolates)内运行,而不是线程。每个隔离区都有自己的内存堆,确保每个隔离区的状态都不会被其他隔离区访问。
Typedefs
在Dart中函数也是对象,就像字符和数字对象一样。使用typedef,或者function-type alias为函数起一个别名,别名可以用来声明字段及返回值类型。
元数据
元数据可以在library、calss、typedef、type parameter、constructor、factory、function、field、parameter或者variable声明前使用,也可以在import 或则export指令前使用。使用反射可以在运行时获取元数据信息。
- @deprecated
- @override
自定义注解
1 | library todo; |
注释
单行注释
1 | //xxxxxxx 编译器会忽略 |
多行注释
1 | /* |
文档注释
1 | /// 这是文档注释[ClassName]会自动生成指向类文档的连接 |