Dart语言基础入门

与 Javascript 的异同

相似点

Dart 和 JavaScript 一样,都是单线程语言,通过事件循环来处理异步任务。

Dart 和 JavaScript 都支持异步编程模型,使用类似的语法(例如,JavaScript 中的 Promise 和 async/await,Dart 中的 Future 和 async/await)来处理非阻塞的异步操作。

Dart 的设计目标之一就是支持跨平台开发,可以在不同的平台上运行。所以 Dart 也是被虚拟机编译成字节码并运行,类似于 JavaScript 在浏览器中运行时被解释执行。

Dart VM 和 V8 都支持 JIT(即时编译)技术,这意味着它们能够将源代码转换为机器代码,并在运行时执行。Dart 虚拟机在不同的操作系统上有不同的实现。

Dart 使用 Pub 作为其包管理系统,类似于 JavaScript 中的 npm。你可以方便地引入和管理依赖项。

Dart 是面向对象语言,但也支持过程式编程和函数式编程的特性。所以完全可以写函数,采用面向过程的编程风格,就像在 JavaScript 中一样。

不同点

Dart 默认启用空安全。JavaScript 不支持空安全。

Dart 是一门面向对象的语言,且是强类型的(但类型注释是可选的,因为 Dart 可以推断类型)。JS 更像是面向过程的,且是弱类型的。

Dart 应用程序是单一入口文件,入口点是 main()函数。

Dart 语法

Dart 官网 dart.dev/language

Dart 在线练习:dartpad.dev/?id

变量 & 常量

Dart 将所有变量的作用域限制在块级别。

变量

定义变量用具体的类型,也可以用 var 自动推断类型,dynamic 或者 Object 表示任何类型。

变量存储的是引用。所以下面名为 "name" 的变量包含对一个值为 "Bob" 的 String 对象的引用。

dart 复制代码
String name = 'Bob' // 主动声明类型
var name = 'Bob'; // 自动推断类型String

常量

final 或者 const 可以定义常量,也能自动推断类型。类的实例常量,只能用 final,不能用 const。一般写代码时都使用 final 即可。

dart 复制代码
final name = 'Bob'; // 也能自动推断
final String nickname = 'Bobby';

延迟初始化

变量在使用前必须初始化。如果不想在定义时就初始化,可以用 late 关键字。

dart 复制代码
late String description;

void main() {
  description = 'Feijoada!';
  print(description);
}

解构

dart 复制代码
var numList = [1, 2, 3];
var [a, b, c] = numList;
print(a + b + c);

var (a, b) = ('left', 'right');

空安全

Dart 语言默认启用空安全,即不可为任何变量或参数赋值为 null。

除非明确告诉 Dart 变量可以为 null,要表明变量可能具有值 null,需添加 ? 到其类型声明中。

dart 复制代码
int? aNullableInt = null;

空感知运算符

跟 js 中使用方式一样。?? 运算符 (空值合并运算符),??= 运算符 (空值合并赋值运算符),?. 运算符 (可选链运算符)。

dart 复制代码
String? value = null;
String result = value ?? "default";
print(result);  // "default"

String? value = null;
value ??= "new";
print(value);  // "new"

级联操作符

..?.. 允许您对同一对象进行一系列操作。?.. 表示对象可能为空,此时这个后面的级联操作不再执行。

dart 复制代码
var paint = Paint()
  ..color = Colors.black
  ..strokeCap = StrokeCap.round
  ..strokeWidth = 5.0;

// 前面的示例相当于以下代码:

var paint = Paint();
paint.color = Colors.black;
paint.strokeCap = StrokeCap.round;
paint.strokeWidth = 5.0;

数据类型

所有值都是对象,也就是可以放入变量中的所有内容都是对象,即使是数字、函数、null。每个对象都是类的实例。除了 null 之外,所有对象都继承自 Object 类。

null 是一个特殊的对象,它不继承自 Object 类。实际上,null 是所有类型的子类型,这意味着 null 可以被赋值给任何对象引用类型,而不会引发类型错误。

常用类型

数字:int, double, num

将变量声明为 num,变量可以同时具有整数和双精度值。

dart 复制代码
int integerNumber = 42;
double floatingPointNumber = 3.14;

num x = 1;
x += 2.5;

字符串:String,${} 用于字符串插值,将表达式插入到字符串文本中。如果使用$字符串需要加反斜杠 \

多行字符串可以直接定义写成多行,或者用三个引号表示多行。

dart 复制代码
String text = 'Hello, Dart!';

var food = 'bread';
var str = 'I eat ${food}';


final s1 = 'String '
    'concatenation'
    " even works over line breaks.";

final s2 = '''
You can create
multiline strings like this one.
''';

布尔值:bool

dart 复制代码
bool isTrue = true;
bool isFalse = false;

数组(有序的集合):List,也可以使扩展运算符 ......?...?表示不为空就展开。也适用于 Set 和 Map。

添加:add,addAll

更新:arr[index] = 'xxx';

插入:insert 或 insertAll。

删除:remove、removeAt、removeLast、removeRange 或 removeWhere。

替换:fillRange、ReplaceRange 或 setRange。

查找:indexOf 或 lastIndexOf。

遍历:for,for-in,forEach,map,where 等。

判断为空:isEmpty,isNotEmpty

dart 复制代码
List<int> numbers = [1, 2, 3, 4, 5];
List<String> names = ['Alice', 'Bob', 'Charlie'];

不重复的元素的集合:Set。注意他是用大括号 {}

添加:add,addAll

删除:remove、removeAll、removeWhere

判断为空:isEmpty,isNotEmpty

dart 复制代码
Set<int> uniqueNumbers = {1, 2, 3, 4, 5};

键值对的集合:Map

是否有 Key:containsKey

添加或者更新:map['xxx'] = 'yyy';

添加另一个 Map:addAll。

删除:remove。

判断为空:isEmpty,isNotEmpty

dart 复制代码
Map<String, dynamic> person = {
  'name': 'John',
  'age': 30,
  'isStudent': false,
};

List,Set,Map 中可以使用 if 和 for。

不可变

使用 const 关键字,则该集合具有不可变性。

dart 复制代码
const fruits = <String>{'apple', 'orange', 'pear'};

也可使用 unmodifiable 方法

dart 复制代码
final _set = Set<String>.unmodifiable(['a', 'b', 'c']);

相等比较

identical 检查是否为相同的对象(相同的引用),而 == 检查是否在逻辑上相等,即它们的内容是否相同。

dart 复制代码
var a = [1, 2, 3];
var b = a;
print(identical(a, b));  // 输出 true,因为a和b引用相同的对象
dart 复制代码
var a = [1, 2, 3];
var b = [1, 2, 3];
print(a == b);  // 输出 true,因为a和b在逻辑上相等

类型推断

Dart 可以使用 var 或者 final 进行推断类型。

dart 复制代码
var arguments = {'argA': 'hello', 'argB': 42}; // Map<String, Object>

如果显式声明类型,则可以这样写

dart 复制代码
Map<String, Object> arguments = {'argA': 'hello', 'argB': 42};

asisis! 可以方便地在运行时检查类型。

任意类型

任意类型可以用 Object 或者 dynamic。dynamic 类型在编译时不进行静态类型检查,而是在运行时进行。

dart 复制代码
Object myObject = "Hello";
print(myObject.toString());  // 输出 "Hello"

dynamic myDynamic = "Hello";
print(myDynamic.toString());  // 输出 "Hello"

泛型

Dart 中支持泛型

dart 复制代码
var names = <String>['Seth', 'Kathy', 'Lars'];
var uniqueNames = <String>{'Seth', 'Kathy', 'Lars'};
var pages = <String, String>{
  'index.html': 'Homepage',
  'robots.txt': 'Hints for web robots',
  'humans.txt': 'We are people, not machines'
};

类型定义

定义类型别名,可以使用类型参数

dart 复制代码
typedef IntList = List<int>;
IntList il = [1, 2, 3];

typedef ListMapper<X> = Map<X, List<X>>;
ListMapper<String> m2 = {}; // 等于 Map<String, List<String>> m2 = {};

获取对象的类型

要在运行时获取对象的类型,您可以使用 runtimeType,它返回一个 Type 对象。

dart 复制代码
print('The type of a is ${a.runtimeType}');

控制语句,流程语句,循环语句

跟 js 一样。

控制语句:if-else, switch-case

流程语句:break, continue

循环语句:for, while, do-while, for-in, forEach

Dart for-in 循环的工作方式类似于 JavaScript for-of。

dart 复制代码
for (final element in list) {
  print(element);
}

if-case 语句

如果值匹配到某个模式,分支将执行。

dart 复制代码
if (pair case [int x, int y]) return Point(x, y);

异常

Dart 提供了 Exception 和 Error 类型,以及许多预定义的子类型。也可以抛出任何非 null 对象(而不仅仅是 Exception 和 Error 对象)作为异常。

dart 复制代码
throw FormatException('Expected at least 1 section');

// 抛出任意对象:
throw 'Out of llamas!';

on 可以捕获指定异常,catch 捕获任意异常。

dart 复制代码
try {
  breedMoreLlamas();
} on OutOfLlamasException { // 不需要异常对象
  buyMoreLlamas();
} on Exception catch (e) { // 可以拿到异常对象
  print('Unknown exception: $e');
} catch (e) { // 捕获所有类型异常
  print('Something really unknown: $e');
}

断言

在开发过程中,使用断言语句 assert,如果执行时这个断言返回的 false,则会中断正常执行。

dart 复制代码
var text = '123';
assert(text != null);

// 如果text是null,下面不会再执行
print(text)

函数

与 JavaScript 类似,可以在任何地方声明函数,无论是在顶层、作为类字段还是在当前的作用域范围。

Dart 中函数也是有作用域范围,也有闭包。

函数尽量声明返回值类型,当然不声明也可以正常运行。

dart 复制代码
// 有返回值
int add(int a, int b) {
  return a + b;
}

// 无返回值
void sayHello() {
  print('Hello!');
}

// 无声明
sayHello() {
  print('Hello!');
}

函数是一等公民

将函数作为参数传给另一个函数

dart 复制代码
void printElement(int element) {
  print(element);
}

var list = [1, 2, 3];

// Pass printElement as a parameter.
list.forEach(printElement);

匿名函数

没有名称的函数,可以将它们作为参数传递给另一个函数,或者从另一个函数返回它们。

dart 复制代码
var list = ['apples', 'bananas', 'oranges'];
list.map((item) {
  return item.toUpperCase();
});

直接将匿名函数赋值给变量

dart 复制代码
var multiply = (int a, int b) => a * b;

箭头函数

Dart 中箭头函数表达式跟 js 略有不同,只有当函数包含单个表达式或 return 语句时才能使用箭头语法。

=> expr{ return expr; } 的简写。

dart 复制代码
var multiply = (int a, int b) => a * b;

可选参数

可选参数必须放在必须参数的后面,且用中括号包裹,且必须具有默认值或标记为可为 null。

dart 复制代码
multiply(int a, [int b = 5, int? c]) {
}

// 函数调用
multiply(3);
multiply(3, 5);
multiply(3, 5, 7);

命名参数

命名参数跟可选参数类似,但是不必像可选参数那样按照定义的顺序提供,可以通过名称来引用它们。同时用大括号括起来。

默认情况下这些也都是可选的,除非它们被标记为必需。

dart 复制代码
multiply(bool x, {required int a, int b = 5, int? c}) {
}

// 函数调用
multiply(false, a: 3);
multiply(false, a: 3, b: 9);
multiply(false, c: 9, a: 3, b: 2);

异步

Future

类似 js 的 Promise,也是用 then 处理成功的回调,但是用 catchError 而不是 catch 来捕获异常。whenComplete 类似于 js 的 finally。

dart 复制代码
Future<String> httpResponseBody = func();

httpResponseBody.then((String value) {
    print('Future resolved to a value: $value');
}).catchError((err) {
    print('Future encountered an error before resolving.');
});

类似于 js 的 Promise.all 等方法,Dart 有 Future.wait 来同时处理多个 Future。同样的也有 any 方法,有一个完成就结束,不管是成功或者失败。

dart 复制代码
var value = await Future.wait([delayedNumber(), delayedString()]);
print(value); // [2, result]

final result = await Future.any([slowInt(), delayedString(), fastInt()]);
// The future of fastInt completes first, others are ignored.
print(result); // 3

Future 也有 value 和 error 方法,来直接返回一个 Future 的成功或者失败的对象。

dart 复制代码
return Future.value(2021);

return Future.error(Exception('Issue'));

Future 可以通过 sync 方法使函数立即执行,而不会导致异步行为。也可以通过 delayed 方法设置延迟一段时间再执行那些回调函数。

Future 实例还有一个 timeout 方法,可以在超过 timeout limit 后不再继续等待成功或者失败的结果。

dart 复制代码
// 立即执行
final result = await Future<int>.sync(() => 12);

// 延迟执行
Future.delayed(const Duration(seconds: 1), () {
  print('One second has passed.'); // Prints after 1 second.
});

// timeout
var result = await waitTask("completed").timeout(const Duration(seconds: 10));
print(result); // Prints "completed" after 5 seconds.
var result = await waitTask("completed").timeout(const Duration(seconds: 1), onTimeout: () => "timeout");
print(result); // Prints "timeout" after 1 second.

async && await

跟 js 一样,可以使用 async 和 await 来处理异步。

使用 try、catch 和 finally 来处理 await 的异常。

dart 复制代码
Future<void> checkVersion() async {
    try {
        var version = await lookUpVersion();
    } catch (e) {
        // ...
    } finally {
        // ...
    }
}

isolate

在应用程序中,所有 Dart 代码都在 isolate 中运行(许多 Dart 应用程序只使用一个 isolate,即主 isolate。)。每个 isolate 都有一个执行线程,并且不与其他 isolate 共享内存和变量,所以为了相互通信,isolate 使用消息传递。

如果任务只是普通的异步等待,不需要大量的 CPU 计算,那么我们可以直接使用 Future 来进行异步处理。如果是 CPU 密集型的任务,比如 IO 操作,可以考虑使用 Isolate 的方式将其放在单独的线程中执行,以避免阻塞主线程。

使用 Isolate.run() 来创建和执行 Isolate。在 Flutter 中,可以使用 compute(),compute 是 Flutter 中对 Isolate.run() 的封装。

dart 复制代码
void download() async{
    const String downloadLink = '下载链接';
    String fileContent = await Isolate.run(() => downloadAndRead(downloadLink));
    print('展示文件内容: $fileContent');
}

// flutter
void download() async{
    const String downloadLink = '下载链接';
    String fileContent = await compute(downloadAndRead,downloadLink);
    print('展示文件内容: $fileContent');
}

面向对象

Dart 是一种面向对象的语言,具有类和继承。每个对象都是一个类的实例。

this 仅在类中使用,并且始终引用当前实例。

构造方法和实例

构造方法可以是默认构造方法或者命名构造方法。默认构造方法与类同名,而命名构造方法具有自定义名称。

在构造方法中,可以使用 this 关键字来引用当前实例,以初始化实例变量。

创建实例不需要 new 关键字。

dart 复制代码
class Person {
  String name;
  int age;

  // 默认构造方法
  Person(this.name, this.age);

  // 命名构造方法
  Person.guest() {
    name = 'Guest';
    age = 18;
  }
}

void main() {
  // 使用默认构造方法创建实例
  Person person1 = Person('Alice', 30);

  // 使用命名构造方法创建实例
  Person person2 = Person.guest();
}

成员变量、成员方法

所有成员变量都会生成隐式 getter 方法,非 final 变量也会生成隐式 setter 方法。

Dart 没有访问修饰符关键字,所有类成员默认都是公共的。要在 Dart 中将类成员设置为私有,请在其名称前添加下划线 (_)。

dart 复制代码
class Person {
  String name; // 成员变量
  int age; // 成员变量

  void sayHello() { // 成员方法
    print('Hello, my name is ${this.name}'); // 使用this引用实例变量
  }
}

this

关键字 this 用于引用当前实例。

this 可以用来区分实例变量和局部变量。

dart 复制代码
class Person {
    String name; // 实例变量

    Person(this.name); // 构造方法

    void printName() {
        String name = 'Local'; // 局部变量
        print(this.name); // 使用this引用实例变量
        print(name); // 打印局部变量
    }
}

静态变量、静态方法

通过 static 关键字来定义静态变量和方法。

dart 复制代码
class MathUtils {
  static const double pi = 3.14; // 静态变量

  static int add(int a, int b) { // 静态方法
    return a + b;
  }
}

抽象类、接口、mixins

抽象类是不能被实例化的类,用于定义一组方法,但不提供这些方法的具体实现。抽象方法是在抽象类中声明但没有实现的方法,它们需要在子类中被重写以提供具体的实现。

抽象方法只能在抽象类或者 mixins 中。

dart 复制代码
// 定义一个抽象类
abstract class Animal {
  // 定义一个抽象方法
  void makeSound();
}

// 定义一个继承自抽象类的子类
class Dog extends Animal {
  // 实现抽象方法
  void makeSound() {
    print('Woof! Woof!');
  }
}

在 Dart 中,接口和抽象类可以看作是相似的概念。接口也是一种抽象类型,用于定义类应该具有哪些方法和属性,但不提供这些方法和属性的具体实现。类可以实现一个或多个接口,以确保它们具有所需的行为。

dart 复制代码
// 定义一个接口
abstract class Shape {
  void draw(); // 定义一个抽象方法
}

// 定义一个实现了接口的类
class Circle implements Shape {
  @override
  void draw() {
    print('Drawing a circle');
  }
}

Mixin 是一种在类中实现代码重用的机制。一个类可以使用多个 mixin。

dart 复制代码
mixin Swim {
  void swim() {
    print('Swimming');
  }
}

class Fish with Swim {
  // Fish类使用Swim mixin中的方法
}

继承

继承是面向对象编程中的重要概念,它允许一个类(子类)获取另一个类(父类)的属性和行为。子类可以继承父类的成员变量和成员方法,并且可以通过重写方法来修改或扩展父类的行为。

子类构造方法需要调 super 引用父类的构造函数。

dart 复制代码
// 定义一个父类
class Animal {
  String name;

  Animal(this.name);

  void eat() {
    print('${this.name} is eating');
  }
}

// 定义一个子类,并继承父类
class Dog extends Animal {
  Dog(super.name); // 等同于:Dog(String name) : super(name);

  void bark() {
    print('Woof!');
  }
}

void main() {
  // 创建子类的实例
  var dog = Dog('Buddy');
  dog.eat(); // 输出 Buddy is eating
  dog.bark(); // 输出 Woof!
}

super 参数

用 super 来初始化参数是一种机制,用于避免在构造函数的 super 调用中手动传递每个参数。这种特性使得在子类构造函数中能够将参数快速地传递给指定或默认的父类构造函数。这对于简化代码、提高可读性非常有用。

dart 复制代码
class Vector2d {
  final double x;
  final double y;

  Vector2d(this.x, this.y);
}

class Vector3d extends Vector2d {
  final double z;

  // Forward the x and y parameters to the default super constructor like:
  // Vector3d(final double x, final double y, this.z) : super(x, y);
  Vector3d(super.x, super.y, this.z);
}

单例模式

有些类提供常量构造函数,使用常量构造函数创建编译时常量,这个实例就是相等的。

dart 复制代码
class MySingleton {
  const MySingleton();

  void doSomething() {
    print('Doing something...');
  }
}
void main() {
    var singleton1 = const MySingleton();
    var singleton2 = const MySingleton();

    print(identical(singleton1, singleton2)); // true
}

导入导出模块

import 和指令 library 可以帮助您创建模块化的代码,每个 Dart 文件都是一个模块(库),即使它不使用 library 指令。以下划线 (_) 开头的标识符仅在模块(库)内部可见。

使用模块(库)

对于内置库,URI 有特殊的 dart:xxx 方案。对于其他库,您可以使用文件系统路径或 package:xxx,package 指定由包管理器(例如 pub 工具)提供的库。

dart 复制代码
import 'dart:html';
import 'package:test/test.dart';

指定库前缀

如果导入两个具有冲突标识符的库,则可以为一个或两个库指定前缀。

dart 复制代码
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

lib2.Element element2 = lib2.Element();

仅导入库的一部分

使用 show 或者 hide 可以选择导入某部分或者不导入某部分。

dart 复制代码
// Import only foo.
import 'package:lib1/lib1.dart' show foo;

// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;

导出

export 关键字用于在导出某个库或者其中的某个方法。

dart 复制代码
// library1.dart
library library1;

void function1() {
  print('Function 1');
}

void function2() {
  print('Function 2');
}

// 使用 export 导出 library1 的内容
export 'library1.dart';

另一个文件中可以导入

dart 复制代码
// main.dart
import 'library1.dart';

void main() {
  function1();  // 输出 'Function 1'
  function2();  // 输出 'Function 2'
}

文件懒加载

仅 dart compile js 支持延迟加载。 Flutter 和 Dart VM 不支持延迟加载。

延迟加载库,必须首先使用 deferred as。然后使用库时需要 await 或者 then

dart 复制代码
import 'package:greetings/hello.dart' deferred as hello;

await hello.loadLibrary();

包搜索:pub.dev/

Dart 包是一个包含 pubspec.yaml 文件的目录,pubspec.yaml 包含一些有关包的元数据。pubspec.lock 会锁包版本。

类似于 package.json。

yaml 复制代码
name: my_app

dependencies:
  js: ^0.6.0
  intl: ^0.17.0

使用 dart pub get 安装依赖,dart pub add xxx 添加依赖。dart pub upgrade 可以更新包版本。

创建包

需要有 pubspec.yaml 文件和 lib 目录。如果 lib 下有 src 目录,lib/src 下的代码被认为是私有的。

lib/<package-name>.dart 是你的主文件,负责导出所有公共的 api。实现文件一般放在 lib/src 下,而不是放在 lib 下,防止被用户直接导入。

dart 复制代码
export 'src/cascade.dart' show Cascade;
export 'src/handler.dart' show Handler;

常用第三方包

  • archive:压缩解压相关。
  • characters:用于处理 Unicode 字符和字符串的库。
  • http:发送请求。
  • path:处理文件路径。
  • yaml:处理 yaml 文件并读取。

下面是一些扩展了 dart 核心功能的包:

async,collection,convert,io

发布包

使用 dart pub publish 命令。

相关推荐
️ 邪神2 小时前
【Android、IOS、Flutter、鸿蒙、ReactNative 】标题栏
android·flutter·ios·鸿蒙·reatnative
Jewel1059 小时前
Flutter代码混淆
android·flutter·ios
一头小火烧19 小时前
flutter打包签名问题
flutter
sunly_19 小时前
Flutter:异步多线程结合
flutter
AiFlutter19 小时前
Flutter网络通信-封装Dio
flutter
B.-19 小时前
Flutter 应用在真机上调试的流程
android·flutter·ios·xcode·android-studio
有趣的杰克19 小时前
Flutter【04】高性能表单架构设计
android·flutter·dart
sunly_1 天前
Flutter:父组件,向子组件传值,子组件向二级页面传值
flutter
爱学习的绿叶1 天前
flutter TabBarView 动态添加删除页面
flutter
趴菜小玩家1 天前
使用 Gradle 插件优化 Flutter Android 插件开发中的 Flutter 依赖缺失问题
android·flutter·gradle