秀发去无踪之你不得不知道的Dart

3,139 阅读7分钟

引言

最近flutter发展的如火如荼,而Dart作为flutter的开发语言,了解一下伐?

语言特征

dart 中一切皆对象 dart这个特征和java很像,所有变量、方法都是对象,那怕仅仅是个数字或字符串

入口函数

在dart项目中,必须声明一个入口函数main,这也很java,程序从main函数开始运行(习惯使用js的需要注意这一点)。

void main(){
  print('hello dart!')
}

变量声明

  • 一般声明变量必须明确类型,类型明确后不能改变,未赋值默认值都是null
    int i = 2;
    i = '3' ; // error:i为int类型,不能赋值字符串类型
    
  • var 可以声明任意类型的变量,赋值后类型不能再改变
    var i;
    i = 2; 
    i = 3;
    i = '4'; // error: var 变量赋值后,类型不能再改变
    
  • dynamic 可以声明动态类型的变量,每次赋值类型不受限制
    dynamic d = 1;
    d = '123'; // 变量类型确定后还可以再赋值其他类型
    
  • final 变量只能赋值一次,声明时必须初始化
    final a = 1;
    a = 2; // error: final变量只能赋值一次,再次赋值报错
    
    final b = [2];
    b[0] = 1;
    b.add(2); // final 虽然不能再次赋值,但是可以改变他成员变量的值
    
  • const 变量是编译时常量,是一种特殊的final变量
    const a = 1;
    a = 2; // error: const变量只能在初始化时赋值
    
    const b = [2];
    b.add(2); // error: const 变量中的成员变量也不可再次改变
    

方法

  • 一般定义方法须明确入参个数、类型及出参的类型
    // 方法定义,明确入参和出参,编译时可以做类型校验,提前发现一些错误
    int add(int m, int n) {
      return m + n;
    }
    void main() {
      int a = add(50, 30);
      String b = add('50', 30); // error: 入参类型和出参类型不正确,编译不通过
    }
    
  • 如果不明确定义方法入参和出参参数类型,编译时无法进行类型检查
    // 出参和入参的类型可以忽略,如果忽略,编译时无法进行类型检查
    add( m, n) {
      return m + n;
    }
    void main() {
      int a = add(50, 30);
      String b = add('50', 30); // 可以通过编译,但在运行时会报异常
    }
    
  • 方法入参也可以使用基于命名或位置的可选参数
    // 可选位置参数
    // 可选参数后面不能再有参数
    int add(int m, [int n]) {
      int sum = m;
      if (n != null) {
        sum += n;
      }
      return sum;
    }
    
    void main() {
      print(add(1));
      print(add(1, 2));
    }
    
    // 可选命名参数
    int add({int m, int n}) {
      int sum = 0;
      if (m != null) {
        sum += m;
      }
      if (n != null) {
        sum += n;
      }
      return sum;
    }
    
    void main() {
      print(add());
      print(add(m: 1));
      print(add(n: 2));
      print(add(m: 1, n: 2));
    }
    
  • 定义方法入参时可以定义可选参数的默认值,默认值必须是编译时常量
    // 默认参数值
    // 默认值只能给可选参数设置
    // 位置可选参数匹配顺序是从左到右的,即便有默认参数值
    int add(int m, [int n = 0, int i]) {
      var sum = m + n;
      if (i != null) {
        sum += i;
      }
      return sum;
    }
    
    void main() {
      print(add(1));
      print(add(1, 2));
    }
    
  • 箭头函数只能跟一个表达式,不能使用语句和代码块(前端开发人员要特别注意,和js的箭头函数不同)
    // => 函数
    void main() {
      var add = (int m, int n) => m + n;
      var sub = (int m, int n) => return m - m; // error: ruturn 是代码语句,箭头函数不能使用代码语句
      add(1 , 2);
    }
    
  • 当方法作为参数传递给高阶方法时,可以使用匿名方法
    // 匿名函数
    void main() {
      var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums'];
      list.forEach((i) {
        print(list.indexOf(i).toString() + ': ' + i);
      });
    }
    
    也可以使用箭头函数为高阶方法传参
    // 匿名函数
    void main() {
      var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums'];
      // 转换为箭头函数时,要去掉语句后面的分号,
      // 有分号就是语句,没有分号就变成了表达式,符合箭头函数要求
      list.forEach((i) => {print(list.indexOf(i).toString() + ': ' + i)});
      // 或
      list.forEach((i) => print(list.indexOf(i).toString() + ': ' + i));
    }
    
  • Dart是静态作用域语言,编译时变量的作用域就已经确定(这点也和js不同,前端开发人员需注意)
    // 静态作用域
    int a = 5;
    class Test {
      int a = 6;
      int getA() {
        return a;
      }
    }
    
    void main() {
      var obj = new Test();
      print(obj.getA()); // 6
      var fun = obj.getA;
      print(fun()); // 6
    }
    

操作符

  • 除法( / ), 整除(~/)
    void main() {
      print('5/2 = ${5 / 2}'); // 5/2 = 2.5
      print('5~/2 = ${5 ~/ 2}'); // 5~/2 = 2
    }
    
  • 类型转换(as), 类型判断(is、is!), as 只能从父类型转子类型, is 判断变量是某种类型, is!和is相反,判断变量不是某种类型
    void main() {
      num a = 123;
      //  print(a.isOdd); // error: isOdd 不是num的成员变量
      print((a as int).isOdd);// isOdd 是int的成员方法,所有a必须转换成int类型后再调用
      print('a is not String ? ${a is! String}'); // a is not String ? true
      print('a is int ? ${a is int}'); // a is int ? true
    }
    
  • ??= 为值为null的变量赋值
    void main() {
      var a;
      a ??= 1; // 赋值前a = null,赋值后 a=1
      a ??= 2; // 赋值前 a = 1, 赋值后 a=1
      print(a); // 1
    }
    
    
  • expr1 ?? expr2, 等价于 expr1 != null ? expr1 : expr2
    void main() {
      var a;
      var b = 1;
      print(a ?? b); // 1
      a = 0;
      print(a ?? b); // 0
    }
    
  • 级联操作(..)可以在同一个对象上连续调用多个函数及访问成员变量
    //  '..'不同于'.',不要求函数有返回值,'..'相当于普通函数执行后返回‘this’
    class Car {
      void move(String direction, double distance) {
        print('小汽车向${direction}行驶了${distance} 千米');
      }
    
      Car run(String direction, double distance) {
        print('小汽车向${direction}行驶了${distance} 千米');
        return this;
      }
    }
    
    void main() {
      Car car = new Car();
      car..move('东', 3)..move('南', 4)..move('西', 4)..move('北', 4);
      car..move('东', 3)..run('西', 3)..run('北', 4).run('西', 4).run('北', 4);
    }
    
  • a?.b 如果a为null,则返回null,否则返回a.b
    void main() {
      int a;
      // print(a.isOdd); // error: a未赋值,不能访问成员变量和成员函数
      print(a?.isOdd);
      // 相当于
      if (a == null) {
        print(null);
      } else {
        print(a.isOdd);
      }
    }
    

  • 每个对象都是一个类的实例,所有的类都集成于Object
    void main() {
      int a = 1;
      print(a is Object); // true
    }
    
  • 每个类都只能有一个父类,但是可以同时实现多个接口,一个父类可以同时被多个子类继承
    class Tiger extends Animal implements Fly, Swim {
      // todo
    }
    class Dog extends Animal {
      // todo
    }
    
  • 每个实例变量都会自动生成一个getter方法,Non-final实例变量还会自动生成一个setter方法
    class Test {
      int a;
      // 相当于
      // int _a;
      // int get a => _a;
      // set a(int val) => this._a = val;
    }
    
  • 定义一个和类名字一样的方法就定义了一个构造函数,还可以带有其他可选的标识符,如果没有定义,会生成一个不带参数的默认构造函数,构造函数不会被继承
    class Parent {
      String name;
      Parent(this.name);
    }
    
    class Child extends Parent {
      Child(String name) : super(name);
      Child.create(String name) : super(name);
    }
    void main(){
      var zhangsan = Child('张三');
      var lisi = Child.create('李四');
    }
    
  • 可以使用abstract来定义抽象类,抽象类不能被实例化。抽象类通常含有抽象方法,抽象方法只有方法签名,没有实现。
    abstract class Animal {
      // 抽象方法
      void moving();
    }
    
  • 用extends来实现继承,用implements来实现接口,使用@override来重新父类方法
    class Animal {
      final String name;
      Animal(this.name);
    
      void moving() {
        print('${this.name}正在移动');
      }
    }
    
    abstract class Fly {
      // 抽象方法
      void fly();
    }
    
    class Tiger extends Animal implements Fly {
      Tiger() : super('老虎');
      @override
      void fly() {
        print('如虎添翼');
      }
    }
    
  • 用static定义静态变量和静态方法
    class Calculator {
      static int Add(int m, int n) {
        return m + n;
      }
    }
    void main(){
      print(Calculator(1, 2));
    }
    

库的使用

  • 通过import来引入库
  • 引用内部库 import 'dart:schema',如:import 'dart:io';
  • 引用外部库 import 'package:schema', 如:import 'package:utils/utils.dart';
  • 可以通过 as 来指定库前缀,如 import 'package:utils/utils.dart' as utils;
  • 可以通过 showhide 来部分导入库
  • 可以通过 deferred as 延迟加载一个库

dart 核心库

  • dart:core 提供了基础类型、集合和其他少量但是非常核心的功能,dart应用会自动导入这个库。
  • dart:convert 提供了一些用来转换 JSON 和 UTF-8 的转换器,还可以自定义 新的转换器。
  • dart:async 异步编程通常使用回调函数,但是 Dart 提供了另外的 选择: Future 和 Stream 对象。
  • dart:html 为基于web的应用提供了操作Dom元素和使用HTML5 API的功能。
  • dart:math 提供了常用的数学运算功能。
  • dart:io 提供了一下文件读写、进程访问、sockets等相关的功能,只有命令行应用可以使用。

PUB

pub类似于node的npm,用于管理dart应用的包,详情参见pub.dev/

总结

了解了上面这些基础语法,基本上就可以看懂一些dart示例和源码了,想有所成,了解这些还是远远不够的,但是这些可以作为一个开始,作为学习dart的起点。

参考

关于我们

快狗打车前端团队专注前端技术分享,定期推送高质量文章,欢迎关注点赞。
文章同步发布在公众号哟,想要第一时间得到最新的资讯,just scan it !

公众号二维码