Dart

0x01 了解 Dart

(1)概述

  • Dart 是一门标准的计算机编程语言,由 Google 于 2011 年推出,专注于改善构建客户端应用程序的体验
    • Dart 属于应用层编程语言,一般运行在 Dart VM 上,特定情况会编译成 Native Code 运行在硬件上
    • Dart 相比 Java 更加易于理解;相比 JavaScript 更加规范和工程化
  • Dart 特性:
    • 语法便捷
    • 面向对象编程,数据类型均由 Object 派生
    • 强类型,可以类型推断
    • 单进程异步事件模型
    • 可以实现多线程,通过独特的隔离区(Isolate)
    • 支持泛型运算符重载
    • 可以简单实现高效代码,通过采用强大的 Future 和 Stream 模型
    • 支持 混入(Mixin) 特性,可以更好的实现方法复用
    • 支持多平台
  • Dart 应用方向:
    • 移动端开发:通过基于 Dart + CPP + Skia 开发的 Flutter 框架实现
    • 服务端开发:即 DartVM 命令行程序
    • Web 端开发:通过 dart2js 可以将 Dart 编译为 JavaScript

(2)环境配置

以 Windows 为例

  1. 安装 Chocolatey,访问链接,选中 Individual,复制命令到以管理员身份运行的 PowerShell 中执行

    使用命令 choco -v 验证是否安装成功

  2. 使用命令 choco install dart-sdk 安装 Dart SDK

    新版本更新后,可以使用命令 choco upgrade dart-sdk 更新

  3. 如果使用 VSCode 进行开发,可以安装插件 Dart

  4. 创建 main.dart 文件,在其中编写简单的 Hello World 程序

    void main() {
      print("Hello World!");
    }
    

    main() 是 Dart 程序的入口方法

0x02 基本语法

(1)变量与常量

  • 使用 vardynamic 声明一个变量,并可以赋予不同类型的值(类似 JavaScript),使用 final 声明只能赋值一次的变量

    void main() {
      var a = 10;
      print(a);	// 10
      // a = "Hello"; // 报错:A value of type 'String' can't be assigned to a variable of type 'int'.
    
      var b;
      b = 10;
      print(b); // 10
      b = "Hello";
      print(b); // Hello
    
      dynamic c;
      print(c); // null
    
      final d = 10;
      print(d);
      // d = 20; // 报错:The final variable 'd' can only be set once.
    }
    
    • 声明时赋值则固定了变量的类型,之后重新赋值时必须为该类型的值
    • 未赋值则默认为 null
  • 使用 const 声明一个常量,且使用 const 声明的必须是编译器常量

    void main() {
      const a = 10;
      print(a);
    }
    

    constfinal 的超集,const 声明的常量是在非运行时就能获取的值

    int getNumber() {
      return 10;
    }
    
    void main() {
      final a = getNumber();
      const b = getNumber(); // 报错:Methods can't be invoked in constant expressions.
      print(a);
      print(b);
    }
    

(2)内置类型

a. 数字类型

  • Dart 支持两种数字类型:

    • int:整数型,范围为 \(-2^{53}\)\(2^{53}\) 之间,如:

      var number = 10;
      var hex = 0xffffff;
      var bigInt = 1234567890123456789;
      
    • double:双精度浮点数,符合 IEEE 754 标准

      var float = 3.14;
      var exponents = 1.42e5;
      
  • intdouble 都是 num 的子类

  • num 类还包括:

    • 基本操作符(如 +- 等)
    • 数学方法(如 abs()floor() 等)
    • 位操作符(如 >>& 等)

b. 字符类型

  • Dart 字符串采用 UTF-16 编码,可以通过单引号 '' 或双引号 "" 来创建字符串

    var string = "SRIGT";
    
  • 字符串中通过 $ 使用变量、通过 ${} 使用表达式

    var string = "SRIGT";
    var output = "My name is $string, I am ${2024 - 2000} years old.";
    // My name is SRIGT, I am 24 years old.
    
  • 可以通过运算符 + 或将多个字符串放在一起,实现字符串的连接

    void main() {
      var string1 = 'Hello' ' ' 'World';
      var string2 = 'Hello' + ' ' + 'World';
      assert(string1 == string2);
    }
    
  • 可以通过三个单引号或三个双引号实现多行字符串

    void main() {
      var string1 = "Hello\n" + "World";
      var string2 = """Hello
    World""";
      assert(string1 == string2);
    }
    
  • 可以通过前缀 r 实现一个原始 raw 字符串

    var string = r"Hello\nWorld";
    

字符串与数字类型转换

  • 字符串与整型

    void main() {
      // 字符串转换为整型
      var number = int.parse("123");
      assert(number == 123);
    
      // 整型转换为字符串
      var string = number.toString();
      assert(string == "123");
    }
    
  • 字符串与浮点型

    void main() {
      // 字符串转换为浮点型
      var number = double.parse("1.23");
      assert(number == 1.23);
    
      // 浮点型转换为字符串
      var string = number.toStringAsFixed(2);
      assert(string == "1.23");
    }
    

c. 布尔类型

  • Dart 的布尔类型包括 truefalse

  • 特别的,Dart 中只有 true 为真,其他均为假

    if (1) print("true");		// 报错:Conditions must have a static type of 'bool'.
    if ("") print("true");		// 报错:Conditions must have a static type of 'bool'.
    if (null) print("true");	// 报错:Conditions must have a static type of 'bool'.
    

    相应的,需要明确比较内容获取布尔值

    void main() {
      assert(1 >= 0);
      assert("".isEmpty);
      assert(null == null);
    }
    

d. 列表(数组)类型

  • Dart 中的列表类型 List 和其他编程语言的数组类型相似

    void main() {
      var list = [1, 2, 3];
      assert(list[0] == 1);
      assert(list.length == 3);
    }
    
  • 通过在列表字面量前添加 const 可以定义以不变的列表常量

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

e. 映射类型

  • Dart 中的映射类型 Map 是一个键值对相关的对象,键值必须唯一

    var teachers = {
      "Alex": "Math",
      "Bob": "English",
      "Charlie": "History"
    };
    
    var students = new Map();
    students["David"] = 101;
    students["Edward"] = 202;
    students["Fred"] = 501;
    

f. Runes 类型

  • Dart 中 Runes 用于在字符串中表示 Unicode 字符,采用 UTF-32 编码

    void main() {
      var runes1 = "\u2665"; // ♥
      var runes2 = new Runes("\u2665");
      assert(runes1 == String.fromCharCodes(runes2));
    }
    
  • 对于 \u 的数字大于 4 位则需要使用大括号 {}

    var runes = "\u{1f600}"; // 😀
    

g. Symbol 类型

  • Dart 中的 Symbol 对象代表 Dart 程序中声明的操作符或标识符

    void main() {
      var symbol1 = #value;
      var symbol2 = Symbol("value");
      assert(symbol1 == symbol2);
    }
    
  • 常用于反射,混淆后的代码中,Symbol 的名字不会被混淆

(3)运算符

描述 运算符
一元后缀 ++ -- () [] . ?.
一元前缀 - ! ~ ++ --
乘法 * / % ~/
加法 + -
移动位运算 << >>
与位运算 &
异或位运算 ^
或位运算 `
关系和类型测试 <= >= < > as is is!
等式 == !=
逻辑与 &&
逻辑或 `
三目运算符 ?:
级联 ..
赋值 = *= /= ~/= %= += -= <<= >>= &= ^= `

(4)流程控制

  • 条件控制

    • ifelse ifelse
    • switchcasedefault

    条件语句中必须明确地使表达式的计算结果为布尔值

  • 循环控制

    • for
    • while
    • dowhile
    • breakcontinue
  • 断言:assert

(5)异常处理

Dart 中采用 try 测试异常,throw 抛出异常,catch 捕获异常,finally 执行清理

void main() {
  try {
    throw Exception('MyError');
  } catch (e) {
    print(e); // Exception: MyError
  } finally {
    print('finally'); // finally
  }
}

(6)方法

a. 定义方法

  • Dart 推荐在公开的 APIs 上使用静态类型定义,也支持忽略类型定义(即使用 dynamic

    bool isOpen() {
      return true;
    }
    
    isClose() {
      return false;
    }
    
  • Dart 支持胖箭头语法(类似 JavaScript 的箭头函数)

    int increase(int i) => i + 1;
    

b. 必要参数和可选参数

  • 在方法的参数列表中,默认参数都是必要的,且必要参数在前,可选参数在后

  • 可选参数包括:

    • 可选命名参数,使用 {},如:

      getValue({a, b}) {
        return a ?? b;
      }
      
      void main() {
        print(getValue(a: 1)); // 1
        print(getValue(b: 2)); // 2
        print(getValue(a: 1, b: 2)); // 1
      }
      
    • 可选位置参数,使用 []

      getValue([a, b]) {
        return "$a ${b ?? ''}";
      }
      
      void main() {
        print(getValue(1)); // 1
        print(getValue(2)); // 2
        print(getValue(1, 2)); // 1 2
      }
      

c. 参数默认值

当可选参数是指定的静态类型时,必须提供默认值,其他参数可以提供默认值

String getTrafficLight({bool red = false, bool green = false}) => green
    ? "green"
    : red
        ? "red"
        : "unknown";

void main() {
  print(getTrafficLight()); // unknown
  print(getTrafficLight(red: true)); // red
  print(getTrafficLight(green: true)); // green
}

d. 匿名方法

  • 匿名方法指没有方法名的方法,又称 lambda 表达式

  • 匿名方法也可以采用胖箭头语法

    void main() {
      var words = const ["hello", "world"];
      words.forEach((item) => print(item.toUpperCase()));
    }
    

e. 函数闭包

闭包是一个方法对象,可以随时随地访问自己作用域内的变量

Function closure() {
  int number = 0;
  return () => number++;
}

void main() {
  var counter = closure();
  print(counter());	// 0
  print(counter());	// 1
  print(counter());	// 2
}

0x03 面向对象

(1)定义类

  • Dart 的类是一种开发者自定义的类型,类中包含数据、数据的操作方法等

    class Person {
      var name;
    
      Person(this.name);
    
      void sayHello() {
        print("Hello $name.");
      }
    }
    
    void main() {
      var student = Person("Alex");
      print(student.name); // Alex
      print(student.runtimeType); // Person
      student.sayHello(); // Hello Alex.
    }
    
  • Dart 的类可以有无数个构造函数,可以重载类中的操作符(详细查看(5)构造方法

  • Dart 的类也是接口,接口是通过抽象类来实现的(详细查看(9)接口

(2)访问控制

  • Dart 中不存在 publicprivate 关键字

  • 类的成员默认都是公开的(public

  • 对于需要私有化的成员, 需要在其名称前加下划线

    class Person {
      var _name;
    
      Person(this._name);
    
      void sayHello() {
        print("Hello $_name.");
      }
    }
    
    void main() {
      var student = Person("Alex");
      student.sayHello(); // Hello Alex.
    }
    

(3)setter 与 getter

用于设置和访问类私有成员

class Person {
  var _name;

  Person();

  // setter
  set name(String name) => _name = name;

  // getter
  String get name => _name;
}

void main() {
  var student = Person();

  student.name = "Alex";
  print(student.name);
}

(4)静态成员

类静态成员属于类本身,不属于实例对象

class Person {
  var name;

  Person(this.name);

  static String type = "Human";
}

void main() {
  var student = Person("Alex");
  // print(student.type); // 报错:The static getter 'type' can't be accessed through an instance.
  print(Person.type); // Human
  Person.type = "Student";
  print(Person.type); // Student
}

(5)构造方法

  • 匿名构造方法只能有一个,语法为:类名称()
  • 命名构造方法语法为:类名称.命名()
class Person {
  var attr;

  // 匿名构造函数
  Person(String name) {
    this.attr = {'name': name};
  }

  // 命名构造函数
  Person.withId(int id) {
    this.attr = {'id': id};
  }
}

void main() {
  var p1 = new Person('Bob');
  var p2 = new Person.withId(1);
  print(p1.attr); // {name: Bob}
  print(p2.attr); // {id: 1}
  print(p1.runtimeType); // Person
  print(p2.runtimeType); // Person
}
  • 常量构造方法语法为:const 类名称()

    • 常用于生成不可改变的对象,此时类中的所有实例属性必须是 final
    class Person {
      final String name;
      final int age;
    
      Person(this.name, this.age);
    }
    
    void main() {
      final student = Person('Bob', 20);
      print(student.name); // Bob
      print(student.age); // 20
    }
    
  • 工厂构造方法语法为:factory 类名称()

    • 基于工厂构造方法创建时,不一定需要创建一个类的新实例
    class Point {
      num x, y;
    
      Point(this.x, this.y);
    
      factory Point.fromJson(Map<String, dynamic> json) =>
          Point(json['x'], json['y']);
    }
    
    void main() {
      Point point = Point.fromJson({'x': 1, 'y': 2});
      print("${point.x}, ${point.y}");
    }
    

(6)仿真函数

  • 仿真函数指将类的对象作为方法使用

  • 类的仿真函数通过特殊方法 call() 实现

    class Person {
      var name;
    
      Person(this.name);
    
      call() {
        print("Hello, my name is $name.");
      }
    }
    
    void main() {
      var student = Person("Alex");
      student(); // Hello, my name is Alex.
    }
    

(7)继承

  • 声明类时,通过 extends 指定被继承的父类
  • 通过 super 继承父类的构造方法
  • 通过 @override 覆写父类的某个方法
class Person {
  var name;

  Person(this.name);

  void sayHello() {
    print("Hello, my name is $name.");
  }
}

class Student extends Person {
  var school;

  Student(String name, this.school) : super(name);

  @override
  void sayHello() {
    print("Hello, my name is $name, and I go to $school.");
  }
}

void main() {
  var student = Student("Alex", "UCLA");
  student.sayHello(); // Hello, my name is Alex, and I go to UCLA.
}

上述继承属于单继承,多继承需要通过(10)混入实现

(8)抽象类

  • 使用 abstract 声明抽象类,通过 implements 实现抽象类
  • 抽象类不能实例化、不能有对象
abstract class Person {
  void sayHello();
}

class Chinese implements Person {
  @override
  void sayHello() {
    print("你好");
  }
}

class American implements Person {
  @override
  void sayHello() {
    print("Hello");
  }
}

void main() {
  var p;
  p = new Chinese();
  p.sayHello(); // 你好
  p = new American();
  p.sayHello(); // Hello
}

(9)接口

接口要求子类实现接口中的所有属性或方法

class Person {
  var name;

  Person(this.name);

  void sayHello() {}
}

class Student implements Person {
  var name;
  var school;

  Student(this.name, this.school);

  void sayHello() {
    print("Hello, my name is $name, and I go to $school.");
  }
}

void main() {
  var student = Student("Alex", "UCLA");
  student.sayHello(); // Hello, my name is Alex, and I go to UCLA.
}

(10)混入(Mixin)

  • 混入用于实现类的多继承

  • 通过 with 实现混入,有以下两种方法:

    • class C extends A with B

      class A {
        getA() => print("a");
      }
      
      mixin B {
        getB() => print("b");
      }
      
      class C extends A with B {
        getC() => print("c");
      }
      
      void main() {
        C().getA();
        C().getB();
        C().getC();
      }
      
    • class A with B, C

      mixin A {
        getA() => print("a");
      }
      
      mixin B {
        getB() => print("b");
      }
      
      class C with A, B {
        getC() => print("c");
      }
      
      void main() {
        C().getA();
        C().getB();
        C().getC();
      }
      

(11)枚举

通过 enum 声明一个枚举

enum Day { monday, tuesday, wednesday, thursday, friday, saturday, sunday }

void main() {
  Day today = Day.monday;
  print('Today is $today.');
}

(12)泛型

泛型用于在实例化对象后指定类成员的静态类型

class Attribute<T> {
  var value;

  Attribute(this.value);

  void getType() {
    print(value.runtimeType);
  }
}

void main() {
  Attribute<String> name = Attribute("");
  Attribute<int> age = Attribute(0);

  name.getType(); // String
  age.getType(); // int
}

0x04 异步编程

(1)async / await

Dart 中的异步通过 asyncawait 实现,与 JavaScript 的 asyncawait 类似

void request() async {
  await Future.delayed(Duration(seconds: 1)); // 等待 1s
  print("Message from request");
}

void main() {
  print("Start");
  request();
  print("End");
}

(2)Future

  • Dart 中的异步回调问题通过 Future 实现,与 JavaScript 的 Promise 类似

    Future<String> request() async {
      await Future.delayed(Duration(seconds: 1)); // 等待 1s
      // throw "Error";
      return "Message from request";
    }
    
    void main() {
      request().then((value) => print(value), onError: (e) => print(e));
    }
    
  • 当同时执行多个请求时,通过 wait 实现等待所有请求结果都返回后,再执行后续操作,类似 JavaScript 的 Promise.all(当队列中某个异步任务抛出异常时,会中断执行并直接进入异常处理阶段)

    Future<String> request1() async {
      await Future.delayed(Duration(seconds: 1)); // 等待 1s
      return "Message from request1";
    }
    
    Future<String> request2() async {
      await Future.delayed(Duration(seconds: 2)); // 等待 2s
      throw "Error2";
      // return "Message from request2";
    }
    
    Future<String> request3() async {
      await Future.delayed(Duration(seconds: 3)); // 等待 3s
      return "Message from request3";
    }
    
    void main() {
      Future.wait([request1(), request2(), request3()]).then((value) {
        print(value);
      }).catchError((error) {
        print(error); // Error2
      });
    }
    
  • Future.then 返回的依然是 Future 类型,可以采用链式调用的方法

    Future<int> request1() async {
      await Future.delayed(Duration(seconds: 1)); // 等待 1s
      return 1;
    }
    
    Future<int> request2(int value) async {
      await Future.delayed(Duration(seconds: 2)); // 等待 2s
      return value + 2;
    }
    
    void main() {
      request1()
          .then((value) => request2(value))
          .then((value) => print(value))
          .catchError((error) => print(error));
    }
    

(3)Stream

  • Stream 是一系列异步事件的序列,类似于一个异步的 Iterable

  • 举例:读取文本文件

    import 'dart:io';
    import 'dart:convert';
    
    void main() {
      Stream<List<int>> stream = new File("./text.txt").openRead();
      stream.transform(utf8.decoder).listen((data) => print(data));
    }
    
  • 详细内容参考官方文档:

0x05 外部依赖

\(包 > 库\)

(1)自定义库

  1. 新建 module.dart

    class Person {
      String name;
      int age;
    
      Person(this.name, this.age);
    
      void printInfo() {
        print('Name: $name - Age: $age');
      }
    }
    
  2. 在 main.dart 中导入并使用 Person

    import 'module.dart';
    
    void main() {
      var student = Person("SRIGT", 18);
      student.printInfo();
    }
    

对于重复命名的变量或方法,可以使用 as 对导入的库设置别名解决

(2)核心库(内置库)

  • 核心库统一采用前缀 dart:库名称,如 import 'dart:core';
  • 常用核心库包括:
    • core:小而关键的内置功能集
    • async:异步编程
    • math:数学
    • convert:具有用于 JSON 和 UTF-8的转换器,以及对创建其他转换器的支持
    • io:处理文件、目录、进程、套接字、 WebSocket 以及 HTTP 客户端和服务器
    • html:编写浏览器程序,操作 DOM 中的对象和元素,并访问 HTML5 API
      • 该库正在被 web 库取代,web 库具有可以兼容 Wasm 等新特性
    • ……

(3)第三方库引用

  • Dart 采用 Pub 包管理系统,官网链接

  • 引用第三方库步骤如下:

    1. 创建 pubspec.yaml

      name: dart_app
      
      dependencies:
        js: ^0.6.0
        intl: ^0.17.0
      

      也可以使用命令 dart pub add 添加包信息到 pubspec.yaml

    2. 使用命令 dart pub get 获取包

    3. 使用前缀 package: 导入包中的库,如 import 'package:js/js.dart' as js;

    4. 使用命令 dart pub upgrade 升级包

-End-

posted @ 2024-11-22 19:12  SRIGT  阅读(5)  评论(0编辑  收藏  举报