Dart 语言基础浅析
Posted 程序员思语
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dart 语言基础浅析相关的知识,希望对你有一定的参考价值。
Dart语言基础总结完结篇,在Fultter项目落地之前,对Dart语言做一次总结,下期将以dart项目为背景直接上代码(bug),具体业务具体分析。
友情提示:阅读本文大概需要 15分钟
前言
Dart
Dart 是谷歌在 2011 年推出的编程语言,是一种结构化 Web 编程语言,允许用户通过 Chromium 中所整合的虚拟机(Dart VM)直接运行 Dart 语言编写的程序。下面将对目前Dart2.1.0版本进行卡片式总结。
Dart语言特性
1.动态类型语言,可以给变量定义一个类型,这样会更严谨更安全(对比TS和JS)。
2.面向对象、并发编程。
3.没有初始化的变量都会被赋予默认值 null。
4.Dart 暂时没有 public、private、protected、# 这些关键字,变量名以""开头意味着对它的库(lib)来说是私有的。
语法卡片
1、内置类型
numbers(数值)
strings(字符串)
booleans(布尔)
lists(列表,也称为数组)
maps(映射)
runes(在字符串中表示一个Unicode字符)
symbols
2、Runes
// 在 Dart 中,runes 表示字符串中 UTF-32 编码的码位。
main() {
var clapping = '\u{1f44f}';
print(clapping);
print(clapping.codeUnits);
print(clapping.runes.toList());
Runes input = new Runes(
'\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}');
print(new String.fromCharCodes(input));
}
3、Symbols
一个 Symbols 对象表示 Dart 程序中已声明的运算符或标识符。你可能永远都不需要用到 symbols,但是它们对于通过名称来标识引用的接口非常重要,因为缩写改变标识符的名字但不改变标识符的 symbols。
要获取一个标识符的 symbol,使用 symbol 字面量,语法是 # 后面跟上标识符:#test
4、函数
Dart 是一个完全的面向对象语言。
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
// 如果忽略了类型,函数依然是可用的
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
// 支持箭头函数
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
// 函数有两种类型的参数:必须参数和可选参数。
// 必须参数在参数列表的前面,可选参数跟在后面。
5、main() 函数
这一点和C语言、C++类似,每一个应用都有一个顶级的 main() 函数作为这个应用的入口。main() 函数返回 void 而且有一个可选的参数,类型为 List
// 例子 web app 里面的 main() 函数:
void main() {
querySelector('#sample_text_id')
..text = 'Click me!'
..onClick.listen(reverseText);
}
// 或者运行命令行中的main函数
// 像这样运行该程序:dart args.dart 1 test
void main(List<String> arguments) {
print(arguments);
assert(arguments.length == 2);
assert(int.parse(arguments[0]) == 1);
assert(arguments[1] == 'test');
}
和其它编程语言相通的,函数可以作为参数传入其他函数中,同时也支持匿名函数、也有闭包、函数作用域等等。
6.类型检查
运算符 as、is 和 is! 可以在运行期方便地进行类型检查。
if (emp is Person) {
// 类型检查
emp.firstName = 'Bob';
}
// 可以使用 as 运算符使代码更简洁
(emp as Person).firstName = 'Bob';
7、级联符号
级联 (..) 允许你在同一个对象上进行一系列操作。除了方法调用,你也可以访问同一个对象的属性。这样通常可以让你免于创建临时变量并且写出更加顺畅的代码。
querySelector('#confirm') // 获取一个对象
..text = 'Confirm' // 使用它的成员
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
// 上面的代码等同于
var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));
// 级联嵌套
final addressBook = (AddressBookBuilder()
..name = 'jenny'
..email = 'jenny@example.com'
..phone = (PhoneNumberBuilder()
..number = '415-555-0100'
..label = 'home')
.build())
.build();
8、流程控制语句
if 和 else、for 循环、while 和 do-while 循环、break 和 continue、switch 和 case、断言(assets)
你也可以使用 try-catch 和 throw 控制流程。
// 如果一个布尔值为 false,使用 断言 语句来中止正常的执行
// 确保变量是非空的值
assert(text != null);
// 确保变量小于100
assert(number < 100);
// 确保变量是一个 https 的 URL
assert(urlString.startsWith('https'));
Dart 提供了 Exception 和 Error 类型,以及众多预定义的子类。当然,你可以定义自己的异常。然而,Dart 程序可以抛出任何非空的对象——而不仅仅是 Exception 和 Error 对象——作为异常。
// 处理抛出超过一种类型异常的代码
try {
breedMoreLlamas();
} on OutOfLlamasException {
// 一个指定的异常
buyMoreLlamas();
} on Exception catch (e) {
// 任何是异常的对象
print('Unknown exception: $e');
} catch (e) {
// 未指定类型,处理所有对象
print('Something really unknown: $e');
}
// 当你的异常处理需要异常对象时使用 catch。
try {
// ···
} on Exception catch (e) {
print('Exception details:\n $e');
} catch (e, s) {
print('Exception details:\n $e');
print('Stack trace:\n $s');
}
// 要部分处理一个异常,而允许它继续传播,使用 rethrow 关键词。
void misbehave() {
try {
dynamic foo = true;
print(foo++); // 运行期异常
} catch (e) {
print('misbehave() partially handled ${e.runtimeType}.');
rethrow; // 允许调用者看到这个异常
}
}
void main() {
try {
misbehave();
} catch (e) {
print('main() finished handling ${e.runtimeType}.');
}
}
9、类
Dart 是一门面向对象的编程语言,具备类和基于混入的继承。每一个对象都是一个类的实例,而所有的类都派生自 Object。“基于混入的继承”意味着虽然每个类(除了 Object)都只有一个父类,但类的主体可以在多个类层级中被复用。可以使用构造函数、工厂模式,这点跟java类似。
// 构造函数
class Point {
num x, y;
// 设置 x 和 y 的语法糖
// 在构造函数体之前执行
Point(this.x, this.y);
// 代理到主构造函数
Point.alongXAxis(num x) : this(x, 0);
}
// 工厂模式
class Logger {
final String name;
bool mute = false;
// _cache 是库内私有的,由于它名字前的 _
static final Map<String, Logger> _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);
}
}
10、Getters 和 setters
Getters 和 setters 是为一个对象的属性提供读写权限的特殊方法。回想每一个实例变量都有一个隐式的 getter,符合条件的还会有一个 setter。你可以通过实现 getters 和 setters 创建额外的属性,使用 get 和 set 关键词。
11、继承
使用 extends 来创建一个子类,使用 super 来引用父类,dart web的目标是支持ES6。
12、枚举
枚举类型,通常被称作 enumerations 或 enums(枚举),是用来表示有固定数量的常量值的一种特殊类。
// 使用 enum 关键词声明一个枚举类型
enum Color { red, green, blue };
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
// 要获取枚举中所有值的列表,使用枚举的 values 常量
List<Color> colors = Color.values;
assert(colors[2] == Color.blue);
// 可以在 switch 语句 中使用枚举
var aColor = Color.blue;
switch (aColor) {
case Color.red:
print('Red as roses!');
break;
case Color.green:
print('Green as grass!');
break;
default: // 没有这个,你会看到一个警告
print(aColor); // 'Color.blue'
}
// 备注1:你不可以继承、混入或实现一个枚举。
// 备注2:你不可以显式实例化一个枚举。
13、混入
混入 (mixin) 是在类的多继承中复用类代码的一种方式。要“使用”混入,使用 with 关键词跟着一个或多个混入的名字。
class Musician extends Performer with Musical {
// ···
}
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
要实现一个混入,创建一个类继承 Object,不声明构造函数。除非你希望 mixin 可用作常规类,否则请使用 mixin 关键字代替 class。举个栗子。
mixin Musical {
bool canPlayPiano = false;
bool canCompose = false;
bool canConduct = false;
void entertainMe() {
if (canPlayPiano) {
print('Playing piano');
} else if (canConduct) {
print('Waving hands');
} else {
print('Humming to self');
}
}
}
要指定只有某些类型可以使用这个混入——比如,这样你的混入就可以调用它没有定义的方法——使用 on 来指定所需的父类。
mixin MusicalPerformer on Musician {
// TODO
}
14、类变量和方法
使用 static 关键词来实现类级别的变量和方法。
静态变量(类变量)对类级别的状态和常数是很有用的。静态变量直到它们被使用才会初始化。静态方法(类方法)不操作实例,因此不能访问 this。因此,对常见或者广泛使用的实用工具和功能,考虑使用顶级函数,而不是静态方法。
15、泛型
泛型通常是类型安全的要求,但它们除了让你的代码可以运行外还有诸多益处:正确地指定泛型类型会产生更好的代码、你可以使用泛型来减少代码重复。
举个栗子:如果你只想让一个列表包含字符串,你可以指定它为 List
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // 错误
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // 正确
作为对照,Java 中的泛型使用“擦除”,意味着泛型信息在运行时被移除。在 Java 中,可以检测一个对象是否是一个 List,但是你不能检测它是否是一个 List
T first<T>(List<T> ts) {
// 做一些初始化工作或者错误检查
T tmp = ts[0];
// 做一些额外的检查或处理
return tmp;
}
这里 first (
16、库
指令 import 和 library 可以帮你创建一个模块化和可共享的代码库。库不仅提供 API,也是一个隐私单位:以下划线 (_) 开头的标识符只在库中可见。”每个 Dart 应用都是一个库“,即使它没有使用 library 指令。关于库的引入和导出在第一篇文章"Dart 语法基础总结(上)"已经详细介绍了,这里不再赘述。
17、异步
Dart 的库充满了返回 Future 或 Stream 对象的函数。这些函数是“异步的”:它们在设置一个可能比较耗时的操作(比如 I/O)后返回,而不去等待操作完成。
关键字 async 和 await 支持异步编程,可以使你用看起来像同步的方式编写异步代码。具体使用方法,和JS的ES6、ES7无异,在前面的文章也已经介绍了。
18、生成器
当我们需要重复地生成一系列的值时,考虑使用一个“生成器函数” (generator function)。Dart 对这类生成器函数有内置的支持:
同步的生成器:返回一个 Iterable 对象。
异步的生成器:返回一个 Stream 对象。
要实现一个同步的生成器函数,使用 sync* 标记函数体,并使用 yield 语句传递值。举个栗子。
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
// 异步的,可使用 async* 标记并使用 yield 语句传递值
Stream<int> asynchronousNaturalsTo(int n) async* {
int k = 0;
while (k < n) yield k++;
}
// 如果你的生成器是递归的,可以使用 yield* 增进它的性能
Iterable<int> naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
}
}
19、可被调用的类
若想使 Dart 类像函数一样可以被调用,实现 call() 方法。在下面的demo中,WannabeFunction 类定义了一个 call() 函数,它接受三个字符串参数并且连接它们,使用一个空格分隔每个字符串,最后附加一个感叹号。
class WannabeFunction {
call(String a, String b, String c) => '$a $b $c!';
}
main() {
var wf = new WannabeFunction();
var out = wf("Hi","there,","gang");
print('$out');
}
Isolates
大部分计算设备,即使在移动平台上,都拥有多核 CPU。要发挥所有这些核心的优势,开发者通常使用共享内存的线程来实现并发执行。然而,共享状态的并发容易出错并且导致复杂的代码。
Dart 的代码在 isolates 中执行,而不是线程。每个 isolate 都有它自己的内存栈,保证了没有其他的 isolate 可以访问。有点协程的味道,但是又不相同。
Typedefs
函数类型别名,在目前dart2.1.0版本,暂且这么定义吧。给函数类型起了一个名字,使你可以在定义字段和返回值类型时使用。在将一个函数类型赋值给一个变量时,一个 typedef 保留了类型信息。
typedef Compare<T> = int Function(T a, T b);
int sort(int a, int b) => a - b;
void main() {
assert(sort is Compare<int>); // True!
}
20、元数据
使用元数据 (metadata) 来给你的代码提供额外的信息。一个元数据注解以字符 @ 开头,后面跟着的要么是编译期常量(比如 deprecated),要么是常量构造函数的调用。有两个注解可应用于所有的 Dart 代码:@deprecated 和 @override。举个栗子。
// 使用 @deprecated 注解
class Television {
/// _已废弃: 使用 [turnOn] 代替_
@deprecated
void activate() {
turnOn();
}
/// 开启 TV 的电源
void turnOn() {...}
}
// 自定义元数据注解
library todo;
class Todo {
final String who;
final String what;
const Todo(this.who, this.what);
}
// 使用 @todo 注解
import 'todo.dart';
@Todo('seth', 'make this do something')
void doSomething() {
print('do something');
}
元数据可以出现在库、类、typedef、类型参数、构造函数、工厂构造函数、函数、字段、参数或变量声明前以及导入导出指令前。你可以在运行期通过反射取回元数据。
Dart 常用内置库
dart:core - 数值、集合、字符串和其他
dart:async - 异步编程
dart:math - 数学和随机
dart:convert - 解码和编码 JSON、UTF-8 和其他
dart:collection
dart:typed_data
dart:developer
dart:isolate
dart:io
dart:ui
这里没有列举所有的内置库。如果你可能想具体了解的包括 dart:collection 和 dart:typed_data,还有像 Dart web 开发库 和 Flutter 库 这样的平台特定库,可以通过 pub 工具获取更多库。库 collection、crypto、http、intl 和 test 仅仅是你可以使用 pub 安装的库的一个样本。更多新的特性将在后续版本中实现,但希望谷歌也不要破坏已有的优良特性,拭目以待。
参考文档
Dart官网 https://www.dartlang.org
最后
今天的 Dart 语言总结就分享到这里,有错误或问题欢迎大家留言,谢谢 ~
以上是关于Dart 语言基础浅析的主要内容,如果未能解决你的问题,请参考以下文章
flutter解决 dart:html 只支持 flutter_web 其他平台编译报错 Avoid using web-only libraries outside Flutter web(代码片段