Dart基础知识

写了挺长一段时间的flutter,却没有系统地学习dart,因此这里整理一下dart语言的基础知识。

<!--more-->

相关代码可以直接在dartpad上运行,参考

  • dart中文文档,大部分概念都是从官网上整理,建议直接阅读官方教程。

1. 数据类型

dart中,所有能够使用变量引用的都是对象, 每个对象都是一个类的实例。在 Dart 中 甚至连 数字、方法和 null 都是对象

1.1. 内置类型

num数字,包含int和double子类

double a = 1.2;
int b = 2;
print(a.floor()); // 数字也是对象,包含对应的方法

num res = a + b; 
print(res);

String字符串,支持在字符串中使用变量和表达式

String name = 'shymean'; // 可以用单引号或双引号
String greet = '$name say hello'; // 可以直接使用$+变量名的形式快速插入变量
String exp = '1 + 1 = ${1 + 1}'; // 可以使用计算表达式

print(greet + '; ' + exp); // 可以通过+号进行字符串拼接

// 可以使用'''或"""符号创建多行字符串
String lines = '''
    ひとつぶの涙は 
    那滴眼泪
    何を伝えようとしてこぼれ落ちたの 
    似要传递什么一般滑下脸颊
  '''; // 网易云音乐刚好随机到这首歌~~
// 使用r前缀创建原始字符串,此时\n不会被转义成换行符
String raw = r"In raw string,  \n doesn't work."; 

bool布尔值

bool flag = true;
// 条件判断的表达式只接收布尔值truefalse,不会将其他表达式转换为布尔值
// 当 Dart 需要一个布尔值的时候,只有 true 对象才被认为是 true。 所有其他的值都是 flase

if(flag) {
    print("yes");
}else {
    print("noe");
}

List列表

List list1 = [1,2,3]; // 直接初始化
List list2 = List(3); // 构造函数初始化
for(int i = 0; i < list2.length; ++i){
    list2[i] = i;
}
// list常用的一些方法
list1.add(4);
list1.addAll([5,6]);
print(list1); [1,2,3,4,5,6];

print(list1.indexOf(1)); // 0 查询元素的位置,索引从0开始
list1.remove(2); // 将值为2的元素移除
print(list1); // [1,3,4,5,6]
list1.removeAt(2);//将索引值为2的元素移除
print(list1); // [1,3,5,6]
list1.sort((a, b){
return b-a;
});
print(list1); // [6,5,3,1]

Map字典

var code = "code"; // key可以是任何类型的对象
Map response = {
    code: 0,
    "message": "success",
    "data": {
        "list": [1,2,3] // value也可以是任何类型的对象
    },
};
print(response[code]); // 根据key去访问value
print(response['error']); // 访问不存在的key,返回null

var key1 = "key1";
response[key1] = 'add key'; // 新增key
response['key2'] =  'add key2'; 

Function函数

// 常规的函数声明方式
bool fn1(num i) {
    return i > 10;
}
// 可以不用显式声明参数类型和返回值
fn2(i) {
return i * 2;
}
// 箭头函数
Function fn3 = (i) => i * 2;
// 函数作为一等公民,可以通过表达式声明函数,且可以将函数赋值给其他变量
Function fn4 = () {
    print("hello fn4");
};
Function fn5 = fn4;

// IIFE
(num i){
    print(i>2);
}(10);

// 可选位置参数,将可选参数放在参数列表最后一个位置的[]中
fn6(x, [y]){
    if(y == null){
        return x;
    }else {
        return x*y;
    }
}
fn6(10, 2); // 20
fn6(10); // 10

// 可选命名参数
fn7({int x, int y}){
    print("x:$x, y:$y");
}
fn7(x:10, y:2); // x:10, y:2
fn7(x:10); // x:10, y:null
fn7(y:2); // x:null, y:2
// 默认参数
fn8([x=1,y = 2]){
    print("x:$x, y:$y");
}
fn8(10); // x:10, y:2
fn9({x= 1, y=2}){
    print("x:$x, y:$y");
}
fn9(y:10); // x:1, y:10

2. 表达式

除了JS中常见的表达式,还新增了一些新的比较有用的表达式

print(5/2); // 2.5,除
print(5~/2); // 2 整除

num a = 10;
print(a is int); // true
print(a is num); // true
print(a is double); // fase
print(a as int); // 类型转换,注意强制类型转换会报错

var s1 = '123';
var s2 = s1 ?? "default"; // 123,如果s1为null,则将其赋值为默认值
print(s2);

var m1 = 1;
var name = m1?.toString(); // 只有当m1不为null时调用toString

assert(10 > 9); // 断言,如果条件表达式不满足条件,则会打断代码的执行
assert((){
// 断言只在检查模式下运行有效,因此可以通过断言来完成特定环境的判断,如增加测试逻辑等
return true;
}());

3. 作用域

3.1. 常规作用域

Dart 是静态作用域语言,变量的作用域在写代码的时候就确定过了,基本上大括号里面定义的变量就 只能在大括号里面访问。

由于函数可以通过传参或者返回值的形式在其他地方调用,根据其作用域的限制,也会产生闭包。不管该函数在何处被调用, 它都可以访问其作用域内 的变量。

int a = 1;
nest1(){
print(a);// 可以访问到其作用域内的变量,此时打印1

nest2(){
    int a = 100; // 内部作用域声明的同名变量会覆盖外部作用域声明的变量

    nest3(){
    print(a); // 闭包会维持当前的作用域
    }
    return nest3; // 将函数通过返回值的形式导出
}

var fn = nest2();

fn(); // 函数在其定义作用域外部调用,仍旧可以访问其作用域内的变量,此时打印100
}
nest1();
// 无法在作用域外访问到nest2和nest3

此外注意dart内不存在JavaScript中声明提前的问题。

3.2. 库

每个 Dart 文件 都可以当做一个库。使用 import 和 library 指令可以帮助你创建 模块化的可分享的代码。

库不仅仅提供 API, 还是一个私有单元:以下划线 (_) 开头的标识符只有在库 内部可见。

// a.dart
int _count = 0;

const VERSION = "1.0";

void hello(){
  print("hello ${++_count} times from a.dart");
}

class Util {
  void test(){
    print("test in ...");
  }
}

然后在其他文件中就可以引入该库了

import 'a.dart';

print(VERSION); // 直接访问库的变量
hello(); // 直接访问库的方法
Util u = Util(); // 访问库中的类
// print(_count); // 无法访问到库中的私有变量

// 如果多个库存在相同的变量或方法,则可能造成命名冲突,可以在引入时指定命名空间
import 'a.dart' as moduleA;

print(moduleA.VERSION); 
moduleA.hello();
moduleA.Util u = moduleA.Util();

4. 类

// 包含抽象方法,需要使用abstract定义为抽象类
abstract class Animal {
  num age;
  String name;
  // 定义了一个与类名相同的方法就相当于定义了构造函数
  // Animal(age, name){
  //   this.age = age; // 只有当名字冲突的时候才使用 this
  //   this.name = name; // dart推荐忽略new和this
  // }

  // 由于上面通过构造函数初始化数据的场景十分常见,因此提供了下面的两种构造函数语法糖
  // Animal(this.age, this.name);// 位置参数

  Animal({this.age, this.name}); // 可选命名参数

  // 命名构造函数
  Animal.fromJson(Map json) {
    age = json['age'];
    name = json['name'];
  }

  walk() {
    print("$name walking.");
  }
  // 抽象函数,由子类实现

  void eat();
}

// 定义一个类继承 Object,该类没有构造函数, 不能调用 super ,则该类就是一个 mixin
class Pet {
  String color;
  void show() {
    print("I have $color color");
  }
}

// 继承Animal,注入Pet混合
class Cat extends Animal with Pet {
  String food; // 扩展父类属性
  static int dieAge = 100; // 静态属性
  // 构造函数不会被继承,需子类自己实现,通过super可调用父类的构造函数
  Cat({color, age, name, this.food}) : super(age: age, name: name) {
    print("parent constructor");
    this.color = color;
  }
  Cat.fromInit(Map json) : food = json['food'] {
    this.color = json['color'];
    print("init");
  }

  // 子类覆盖父类的方法
  walk() {
    print("I‘m a cat");
    super.walk(); // 调用父类的方法
    print("oh~~");
  }

  // 子类实现父类的抽象方法
  eat() {
    print("$name eat food: $food");
  }

  // 静态方法
  static die(){
    // 静态方法中不能调用实例属性和方法
    print("cat die in age:$dieAge");
  }
}

// Animal animal = new Animal.fromJson({"name":"xx", "age":12}); // 抽象类无法被实例化
Cat cat = Cat(age: 1, color: "red", name: "Tom", food: "fish"); // 调用构造函数,可以省略new

cat.walk(); // 调用实例方法
cat.eat(); // 调用实现的父类抽象方法
cat.show(); // 调用混合的方法

cat.name; // 每个属性都包含了一个默认的getter
cat.name = "Jimmy"; // 如果变量不是final,则可以调用setter

5. 异步支持

除了回调函数之外,dart也支持通过Future控制异步流程,Future与JavaScript中的Promise非常相似。

print("start testAsync");

Future.delayed(new Duration(seconds: 1),(){
  return true;
//  throw AssertionError("Error");
}).then((data){
  //执行成功会走到这里 
  print(data);
}).catchError((e){
  //执行失败会走到这里   
  print(e);
}).whenComplete((){
  //无论成功或失败都会走到这里
  print("done");
});

此外,dart也支持asyn和await控制异步,其使用方式与JavaScript也基本类似

void test() async{
    String res = await Future.delayed(new Duration(seconds: 2),(){
        return "finish";
    });
    print(res);
}

6. 小结

dart除了flutter之外,还有很多其他的应用,如服务端、脚本、web上都可以使用,这是一门非常有趣的语言,可以进一步深入一下,多学点东西总是没坏处的~