梦想还是要有的,万一忘了咋办?

0%

Dart

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
2
3
4
5
6
var name="lisi";
String name="lisi";
final name="lisi";
final String name="lisi";
const name="lisi";
const String name="lisi";

内建数据类型

  • Number
  • String
  • Boolean
  • List
  • Map
  • Set
  • Rune(用于在字符串中标识Unicode字符)
  • Symbool

这些类型都可以被初始化为字面量。例如,‘this is a string’ 是一个字符串的字面量,true 是一个布尔的字面量。
因为 在Dart中所有变量都是一个对象,所以变量可以使用构造函数进行初始化。

函数

函数也是对象,类型是Function。可赋值给变量或作为参数传递给其他函数。

箭头语法(=>)

{return expr;}可以简写为=>expr;

1
2
3
int m(int a){return a+1;}
可以写成:
int m(int a)=> return a+1;

可选参数

可选参数可以是命名参数或者位置参数,但一个参数只能选择其中一种方式修饰。

命名可选参数

调用函数时,可以使用指定命名参数paramsName:value.例如
method1(bold:true,hidden:false);
定义函数是,{param1,param2,……}来命名参数
void method1({boolean bold,boolean hidden});

位置可选参数

1
2
3
4
5
6
7
8
9
10
String say(String from,String msg,[String device]){
var result='$from syys $msg';
if(device!=null){
result='$result with a $device';
}
return result;
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
assert(say('Bob', 'Howdy', 'smoke signal') ==
'Bob says Howdy with a smoke signal');

默认参数值

在定义方法的时候,可以使用 = 来定义可选参数的默认值,未定义 默认null。

1
2
void m1(boolean bold=false,bool hidden=true){...}
m1(bold:true);

主意:在Dart旧版本参数只支持:符号,所以采用(:)进行赋值,新版本已经弃用(:)。

main函数

任何应用必须有一个顶级的main()函数,作为应用服务的入口。返回void,参数:可选List

1
2
3
void main(){
...
}

函数是一等对象

一个函数可以作为另一个函数的参数。例如:

1
2
3
4
5
6
7
void printE(var a){
print(a);
}

var list=[1,2,3,4];

list.forEach(printE);

一个函数赋值给一个变量。例如:

1
2
var m1=(msg)=>'${msg.toUpperCase}';
assert(m1('hello')=='HELLO');

匿名函数

1
2
3
4
5
6
7
8
9
10
11
([[Type] param1[,……]]){
...
}

var list=["a",'b','c','a'];

list.forEach((item){
print('${list.indexOf(item)}:$item');
});

list.forEach((item)=>print('${list.indexOf(item)}:$item'));

作用域

Dart是一门词法作用域的编程语言,就意味着变量作用域是固定的,简单说变量的作用域在编写代码的时候就已经确定了。花括号内的是变量可见的作用域。

闭包

返回值

所有函数都有返回值,如果没有明确指定返回值,函数体会隐式添加return null;语句。

1
2
foo(){}
assert(foo()==null);

运算符

下表是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
    2
    throw FormatException('xxx');
    throw 'Out of llamas';
    提示:规范建议只抛出 Exception、Error 异常。

Dart 是一种基于类和mixin继承机制的面向对象语言。每个对象都是一个类的实例,所有类都继承于 Object。基于Mixin继承意味着每个类都只有一个超类,一个类中的代码可以在其他多个继承类中重复使用。

成员变量

  • 使用(.)来引用实例对象、方法。
  • 使用(?.)可以避免因为左边的对象可能为null,导致的异常。
    1
    2
    3
    4
    5
    6
    var p=Point(2,2);
    p.y=3;
    assert(p.y==3);

    //不会抛出异常
    p2?.y=5;

构造函数

通过构造函数创建对象。构造函数名字是ClassName 或者 ClassName.identifier。例如

1
2
var p1=Point(2,2);
var p2=Point.fromJson({'x':1,'y':2});

版本提示:在Dart2中 new关键字可选。

获取对象类型

使用对象的runttimeType属性,返回Type对象。

1
print('The type of a is ${a.runtimeType}');

实例变量

未初始化实例变量 都为null。
所有实例变量生产隐式getter方法。非final的实例变量同样生成隐式setter方法(不是通过get方法调用哦,是通过(.))。

1
2
3
4
5
class Point{
num x;//初始 null
   num y;//初始 null
   num z=0;//初始 0
}

调用父类非默认构造函数

默认情况,子类的构造函数会在函数开始时 调用父类的默认构造函数(匿名、无参数)。。

  1. initializer list(初始化参数列表)
  2. superclass’s no-arg constructor(父类无名构造函数)
  3. main class’s no-arg constructor (主类无名构造函数)

如果父类没有匿名无参构造函数,需要手动调用其它构造函数。在当前构造函数冒号(:)之后,函数体之前。

1
2
3
Employee.fromJson(Map data):super.fromJson(data){
......
}

重定向构造函数

1
2
3
4
5
class Point{
num x,y;
Point(this.x,this.y);
Point.alongXAxis(num x):this(x,0);
}

常量构造函数

如果该类生成的对象是固定不变的,那么可以把这些对象定义为编译时常量。为此需要定义一个const构造函数,并声明所有实例变量 final。

1
2
3
4
5
6
7
class ImmutablePoint{
static final ImmutablePoint origin=const ImmutablePoint(0,0);
final num x,y;
const ImmutablePoint(this.x,this.y);


}

工厂构造函数

当执行构造函数并不需要总是创建这个类的一个新实例时,则使用factory关键字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Logger{
final String name;
bool mute=false;
static final Map _cache=<String,Logger>{};
factory Logger(String name){
if(_cache.containsKey(name)){
return _cache[name];
}else{
final logger=Logger._internal(name);
_cache[name]=logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg){
if(!mute) print(msg);
}
}

提示:工厂构造函数无法访问this。

方法

方法是为对象提供行为的函数。

实例方法

对象的实例放阿飞可以使用this 和实例变量。

1
2
3
4
5
6
7
8
9
class Point{
num x,y;
Point(this.x,this.y);
num distanceTo(Point other){
var dx=x-other.x;
var dy=y-other.y;
return sqrt(dx*dx+dy*dy);
}
}

Getter、Setter

Getter、Setter是用于对象属性读和写的特殊方法。回想之前的例子,每个实例变量都有一个隐式的getter,通常情况下还有一个setter。

抽象方法

1
2
3
4
5
6
7
8
9
abstract class Doer{
void doSth();//定义一个抽象方法。
}

class EffectiveDoer extends Doer{
void doSth(){
.....
}
}

隐式接口

重写类成员

@override注解指出想要重写的成员

1
2
3
4
class A extends B{
@override
void m(){...}
}

重写运算符

这是一个神奇的功能,
举例重写 +、-操作符

1
2
3
4
5
6
7
8
9
10
11
12
13
class Vector {
final int x, y;
Vector(this.x, this.y);
Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
Vector operator -(Vector v) => Vector(x - v.x, y - v.y);
}

void main() {
final v = Vector(2, 3);
final w = Vector(1, 4);
assert(v + w == Vector(3, 7));
assert(v - w == Vector(1, -1));
}

重写noSuchMethod()

当代码尝试使用不存在的方法或实例变量时,通过重写noSuchMethod方法,来实现检测和应对处理。

枚举类型

使用枚举

枚举中每个值都有一个index getter方法,从0开始。

1
2
3
enum Color{red,green,blue}

assert(Color.red.index==0);

switch语句 如果不处理所有的枚举,会收到警告;

限制:

  • 不能被子类化,混合或实现
  • 不能被显示实例化

为 类添加功能:Mixin

Mixin 是复用类代码的一种途径,复用的类可以在不同层级,之间可以不存在继承关系。
通过with后面跟一个或多个混入的名称,来使用Mixin,例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Musician extends Performer with Musical{...}

class Maestro extends Person with Musical,Aggressive,Demented{
Maestro(String maestroName){
name=maestroName;
canConduct=true;
}
}

mixin Musical{
boolean canPlayPiano=flase;
boolean canCompose=false;
   boolean canConduct=false;

void entertainMe(){
if(canPlayPiano){
print('Playing piano');
}else if(canConduct){
print('Waving hands');
}else {
print('Humming to self');
}
}
}

指定某些类型可以使用Mixin,使用on来指定可以使用Mixin的父类类型:

1
2
3
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
    2
    3
    var names=<String>['a','b','c'];
    var uniqueNames=<String>{'a','b','v'}
    var pages=<String,String>{'a':'a','b':'b'};

    泛型类型的构造函数

1
2
var nameSet=Set<String>.from(names);
var views=Map&lgtint,View>();

运行时中的泛型集合

Dart中的泛型类型是固化的,也就是说它们在运行时是携带者类型信息的。例如,在运行时检测集合的类型:

1
2
3
var names=List<String>();
names.addAll(['a','b']);
print()names is List<String>);

相反,Java中的泛型会被擦除,也就是说在运行时泛型类型参数的信息时不存在的。在Java中,可以测试对象是否为List类型,但无法测试它们是否为List<String>.

限制泛型类型

使用泛型类型的时候,可以使用extends实现参数类型的限制。

1
2
3
4
5
6
7
8
class Foo<T extends SomeBaseClass>{
String toString()=>"Instance of 'Foo<$T>'";
}

class Extender extends SomeBaseClass{}

var SomeBaseClassFoo=Foo<SomeBaseClass>();
var extenderFoo=Foo<Extender>();

泛型函数

最初,Dart的泛型只能用于类。新语法_泛型方法_,允许在方法和函数上使用此类参数:

1
2
3
4
5
T first<T>(List<T> ts){
T tmp=ts[0];
return tmp;

}

库的和可见性

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
2
3
4
import 'package:lib1/lib1.dart' ;
import 'package:lib2/lib2.dart' as lib2;
Element ele1=Element();
lib2.Element ele2=lib2.Element();

导入库的一部分

1
2
import 'package:lib1/lib1.dart' show foo;
import 'package:lib2/lib2.dart' hide foo;

延迟加载库

Deferred loading(也称lazy loading)可以让应用在需要的时候再加载库。

  • 减少app启动时间
  • 执行A/B测试
  • 加载很少使用的功能,例如可选的屏幕和对话。
1
2
3
4
5
6
7
import 'pacakge:greetings/hello.dart' deferred as hello;
//当需要调用的时候,使用库标识符调用loadLibrary()函数来加载库:

Future greet() async{
await hello.loadLibrary();
hello.printGreeting();
}

实现库

  • 如何组织库的源文件。
  • 如何使用export命令。
  • 合适使用part命令。
  • 合适使用library命令。

异步

Dart库包含许多返回Future或Stream对象的函数。这些函数时在设置完耗时任务后,就立刻返回了,不会等待耗时任务完成。使用async和await关键字实现异步编程。可以让你向写同步代码一样实现异步操作。

Future

获取Future执行完成的结果:

  • 使用async 和 await。
  • 使用 FutureAPI
    1
    2
    3
    4
    5
    6
    await lookUpVersion();
    //要使用await,代码必须在异步函数中:

    Future checkVersion() async{
    var version=await lookUpVersion();
    }

    虽然异步函数可能会执行耗时的操作,但它不会等待这些操作。相反,异步函数只有在遇到第一个await表达式时才会执行。也就是说,它返回一个Future对象,仅在await表达式完成后才恢复执行。

声明异步函数

函数体被async标识符标记的函数,即是一个 异步函数。例如:

1
2
3
4
String lookUpVersion()=>'1.0.0';
// 的异步函数是
//Dart会创建Future对象,没有返回值时 Future<void>
Future<String> lookUpVersion() async=> '1.0.0';

Stream

获取Stream数据:

  • 使用 async 和一个异步循环(await for).
  • 使用Stream API
1
2
3
await for (varOrType identifier in expression){
...
}

上面表达式返回的值必须是Stream类型。流程如下:

  • 等待,直到流发出一个值。
  • 执行for循环体,将变量设置为该出发的值
  • 重复1和2,直到关闭流。

要在应用程序的函数中使用异步for循环,函数必须标记async:

1
2
3
4
5
6
Future method() async{

await for (var request in requestServer){
handleRequest(request);
}
}

可调用类

通过实现类的call()方法,函数接收三个字符串参数,函数体将三个字符串拼接。

1
2
3
4
5
6
7
8
class M{
call(String a,String b,String c)=> '$a $b $c';
}
main(){
var m=M();
var out=m('a','b','c');
print('$out');
}

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
2
3
4
5
6
7
8
9
10
11
library todo;
class Todo{
final String who;
final String what;
const Todo(this.who,this.what);
}

@Todo('li','doSth')
void doSth(){
print('do something');
}

注释

单行注释

1
//xxxxxxx 编译器会忽略

多行注释

1
2
3
/*
编译器忽略,不会忽略文档注释。
*/

文档注释

1
2
3
4
/// 这是文档注释[ClassName]会自动生成指向类文档的连接
/// 文档注释
/** 也是文档注释
*/