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 MusicalAggressiveDemented 
{
  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

起初,Dart 的泛型支持仅限于类。一个新的语法,称为“泛型方法“,允许在方法上使用类型参数。

T first<T>(List<T> ts) {
  // 做一些初始化工作或者错误检查
  T tmp = ts[0];
  // 做一些额外的检查或处理
  return tmp;
}

这里 first ( ) 中的泛型参数允许你在以下几个地方使用类型参数 T: 在函数的返回类型中 (T) 、在参数的类型中 (List ) 、在局部变量的类型中 (T tmp)。

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(代码片段

线程浅析

java基础入门-多线程同步浅析-以银行转账为样例

浅析普通函数与构造函数

Dart 语言在 VS 代码中不能执行

浅析XSS的几种测试方法