分享

Dart语言精简入门介绍

 陈喻 2022-03-31

Dart语言精简入门介绍

1、介绍

  • Dart 在设计时应该是同时借鉴了 Java 和 JavaScript和kotlin

  • 面向对象

  • JIT&AOT:JIT(Just in Time)优点:即时编译,开发期间更快编译,更快的重载;缺点:在运行时将代码编译成机器码,给用户最直接的感受就是慢;AOT(Ahead Of Time)事前编译,release期间更快更流畅,典型的例子就是C和C++,它能够直接编译机器码即二进制代码,所以它的加载和执行速度是非常快的。你会发现在开发期间安装Flutter的时候会有一些卡顿,但是在release就是发布之后会变好,这种模式使得APP在发布之后能够以更快的速度运行。

2、基本数据类型

  • 数字类型(num、int和double)

    1)、num:

    Dart数字类型的父类,它既接受浮点类型,也接受整数类型

    num num1 = -1.0; //num是数字类型的父类
    num num2 = 2; //num是数字类型的父类
    

    2)、int、double

    num有两个子类,一个是int,一个是double,int类型只能接收整数,double类型是双精度

    int int1 = 3; //子类一 int类型 只能接收整数
    double d1 = 2.22; //子类二 双精度
    
  • 字符串(String)

    在dart中定义字符串可以用单引号也可以用双引号,一行可以定义一个字符串,也可以定义多个字符串,中间用逗号隔开:

    String str1 = '字符串', str2 = "双引号字符串"; //字符串的定义
    

    在dart中如何进行字符串拼接直接使用加号“+”拼接,当然还有另外一种方法,在上面数字类型的讲解中也有使用到,我们可以使用"$"加上变量名去引用一个变量,这个变量可以是数字类型当然也可以是字符串类型,这个和kotlin语言有点像

    String str3 = 'str1:$str1 str2:$str2'; //字符串拼接
    String str4 = 'str1:' + str1 + "str2:" + str2; //字符串拼接
    String s = "this is dart2";
    String s2 = 'this is dart2';
    // 使用==判断字符串是否相等
    print(s == s2); // true
    

    字符串的方法和java里面的字符串方法类似

  • 布尔

    Dart是强bool类型检查,只有bool类型的值是true,才被认为是true

  • 集合之List

    在Dart中定义集合使用List关键字,List表明集合是个泛型,这里可以传入任何数据类型。List集合可以使用“[]”进行初始化,例如:List list = [1,2,'测试’]; 这种形式,这里没使用泛型所以中括号里面添加元素时既可以传入数字也可以传入字符串,如果需要指定泛型类型,例如:List list1 = []; 这种形式下,添加元素的时候只能添加int类型的数据。

    List list = [1, 2, 3, '集合']; //初始化添加元素
    List list3 = [];
    list3.add('list3'); //通过add方法添加元素
    list3.addAll(list);
    print(list3);
    

    List集合的遍历

    第一种:没有语言之分的for循环方式:

    for (int i = 0; i < list.length; i++) {
        print(list[i]);
    }
    

    第二种:for…in…,同样是for,但是表达式内部我们做一点修改,使用var关键字定义一个变量,然后使用in关键字在集合中进行遍历:

    for (var o in list) {
       print(o);
    }
    

    第三种:forEach()循环,将函数应用于集合的每个元素,括号内传入的为集合的元素:

    list.forEach((val) {
        print(val);
    });
    
  • 集合之Map

    Map集合的初始化

    Map是将key和value相关联的对象,key和value都可以是任意类型的对象,并且key是唯一的,如果key重复,后面添加的key会替换前面的内容。

    ///Map初始化
    Map names = {'xiaoming': '小明', "xiaohong": "小红"};
    print(names);
    Map ages = {};
    ages['xiaoming'] = 12;
    ages['xiaohong'] = 18;
    print(ages);
    

    Map的遍历

    它的里面需要一个回调函数,回调函数里面会输出我们的key和value

    ///Map遍历方式
    ages.forEach((k, v) {
        print('$k,$v');
    });
    

    map()方法,我们可以调用Map集合的map()方法遍历生成一个新的map集合,这个方法接收一个回调函数,入参为key、value,函数内部必须接受一个return,也就是说它会返回一个新的map,这里通过MapEntry来返回之前Map里面每一项的元素,我这里将原map的key和value进行颠倒,那么就可以得到一个相反的集合

    Map ages2 = ages.map((k, v) {
        return MapEntry(v, k);
    });
    print(ages2);
    

    第三种:for循环

    首先循环遍历map集合中所有的key,可以通过map.keys获取,它返回的是一个所有key值的数组(同样的也有map.values),然后我们就可以通过map[key]获取集合中的每一个元素了,代码如下:

    for (var key in ages.keys) {
        print('$key , ${ages[key]}');
    }
    

3、变量声明

  • var

    类似于 JavaScript 中的var,它可以接收任何类型的变量,但最大的不同是 Dart 中 var 变量一旦赋值,类型便会确定,则不能再改变其类型,如:

    var t = "hi world";
    // 下面代码在dart中会报错,因为变量t的类型已经确定为String,
    // 类型一旦确定后则不能再更改其类型。
    t = 1000;
    
  • dynamicObject

    Object 是 Dart 所有对象的根基类,也就是说在 Dart 中所有类型都是Object的子类(包括Function和Null),所以任何类型的数据都可以赋值给Object声明的对象。 dynamicObject声明的变量都可以赋值任意对象,且后期可以改变赋值的类型,这和 var 是不同的,如:

    dynamic t;
    Object x;
    t = "hi world";
    x = 'Hello Object';
    //下面代码没有问题
    t = 1000;
    x = 100
    

    dynamicObject不同的是dynamic声明的对象编译器会提供所有可能的组合,而Object声明的对象只能使用 Object 的属性与方法, 否则编译器会报错

     dynamic a;
     Object b = "";
     main() {
       a = "";
       printLengths();
     }   
    
     printLengths() {
       // 正常
     print(a.length);
       // 报错 The getter 'length' is not defined for the class 'Object'
       print(b.length);
     }
    

4、常量和空安全

  • finalconst

    如果您从未打算更改一个变量,那么使用 finalconst,不是var,也不是一个类型。 一个 final 变量只能被设置一次,两者区别在于:const 变量是一个编译时常量(编译时直接替换为常量值),final变量在第一次使用时被初始化。被final或者const修饰的变量,变量类型可以省略

    //可以省略String这个类型声明
    final str = "hi world";
    //final String str = "hi world"; 
    const str1 = "hi world";
    //const String str1 = "hi world";
    
  • 空安全(null-safety)

    Dart 中一切都是对象,这意味着如果我们定义一个数字,在初始化它之前如果我们使用了它,假如没有某种检查机制,则不会报错,比如:

    test() {
      int i; 
      print(i*8);
    }
    

    在 Dart 引入空安全之前,上面代码在执行前不会报错,但会触发一个运行时错误,原因是 i 的值为 null 。但现在有了空安全,则定义变量时我们可以指定变量是可空还是不可空。

    int i = 8; //默认为不可空,必须在定义时初始化。
    int? j; // 定义为可空类型,对于可空变量,我们在使用前必须判空。
    
    // 如果我们预期变量不能为空,但在定义时不能确定其初始值,则可以加上late关键字,
    // 表示会稍后初始化,但是在正式使用它之前必须得保证初始化过了,否则会报错
    late int k;
    k=9;
    

    如果一个变量我们定义为可空类型,在某些情况下即使我们给它赋值过了,但是预处理器仍然有可能识别不出,这时我们就要显式(通过在变量后面加一个”!“符号)告诉预处理器它已经不是null了,比如:

    class Test{
      int? i;
      Function? fun;
      say(){
        if(i!=null) {
          print(i! * 8); //因为已经判过空,所以能走到这 i 必不为null,如果没有显式申明,则 IDE 会报错
        }
        if(fun!=null){
          fun!(); // 同上
        }
      }
    }
    

    上面中如果函数变量可空时,调用的时候可以用语法糖:

    fun?.call() // fun 不为空时则会被调用
    

    这里和kotlin有点类似

5、函数

Dart是一种真正的面向对象的语言,所以即使是函数也是对象,并且有一个类型Function。这意味着函数可以赋值给变量或作为参数传递给其他函数,这是函数式编程的典型特征。

  • 函数声明

    bool isNoble(int atomicNumber) {
      return _nobleGases[atomicNumber] != null;
    }
    

    这里函数声明跟java或javascript有点类似

    对于只包含一个表达式的函数,可以使用简写语法:

    bool isNoble (int atomicNumber)=> true ;   
    
  • 函数作为变量

    var say = (str){
      print(str);
    };
    say("hi world");
    
  • 函数作为参数传递

    void execute(var callback) {
        callback();
    }
    execute(() => print("xxx"))
    
  • 可选的位置参数

    String say(String from, String msg, [String device]) {
      var result = '$from says $msg';
      if (device != null) {
        result = '$result with a $device';
      }
      return result;
    }
    say('Bob', 'Howdy'); //结果是: Bob says Howdy
    say('Bob', 'Howdy', 'smoke signal'); //结果是:Bob says Howdy with a smoke signal
    
    
  • 可选的命名参数

    定义函数时,使用{param1, param2, …},放在参数列表的最后面,用于指定命名参数。例如:

    //设置[bold]和[hidden]标志
    void enableFlags({bool bold, bool hidden}) {
        // ... 
    }
    

    调用函数时,可以使用指定命名参数。例如:paramName: value

    enableFlags(bold: true, hidden: false);
    

    可选命名参数在Flutter中使用非常多。注意,不能同时使用可选的位置参数和可选的命名参数

6、类和继承

Dart中的继承:

1.子类使用extends关键词来继承父类

2.子类会继承父类里面可见的属性和方法,但是不会继承构造函数

3.子类能复写父类的方法 getter和setter

4.Dart 是不支持多继承的,类和接口是统一的,类就是接口。

一般情况使用抽象类作为接口

void main() {
  new Student().run();
}

abstract class Person {
  void run();
}

class Student implements Person {
  @override
  void run() {
    print("....");
  }
}

7、异步的支持

Dart类库有非常多的返回Future或者Stream对象的函数。 这些函数被称为异步函数:它们只会在设置好一些耗时操作之后返回,比如像 IO操作。而不是等到这个操作完成。

  • Future

    Future与JavaScript中的Promise非常相似,表示一个异步操作的最终完成(或失败)及其结果值的表示。简单来说,它就是用于处理异步操作的,异步处理成功了就执行成功的操作,异步处理失败了就捕获错误或者停止后续操作。一个Future只会对应一个结果,要么成功,要么失败。

    由于本身功能较多,这里我们只介绍其常用的API及特性。还有,请记住,Future 的所有API的返回值仍然是一个Future对象,所以可以很方便的进行链式调用

    1)、Future.then

    为了方便示例,在本例中我们使用Future.delayed 创建了一个延时任务(实际场景会是一个真正的耗时任务,比如一次网络请求),即2秒后返回结果字符串"hi world!",然后我们在then中接收异步结果并打印结果,代码如下:

    Future.delayed(Duration(seconds: 2),(){
       return "hi world!";
    }).then((data){
       print(data);
    });
    

    2)、Future.catchError

    Future.delayed(Duration(seconds: 2),(){
       //return "hi world!";
       throw AssertionError("Error");  
    }).then((data){
       //执行成功会走到这里  
       print("success");
    }).catchError((e){
       //执行失败会走到这里  
       print(e);
    });
    

    3)、Future.whenComplete

    有些时候,我们会遇到无论异步任务执行成功或失败都需要做一些事的场景,比如在网络请求前弹出加载对话框,在请求结束后关闭对话框。这种场景,有两种方法,第一种是分别在thencatch中关闭一下对话框,第二种就是使用FuturewhenComplete回调,我们将上面示例改一下:

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

    4)、Future.wait

    有些时候,我们需要等待多个异步任务都执行结束后才进行一些操作,比如我们有一个界面,需要先分别从两个网络接口获取数据,获取成功后,我们需要将两个接口数据进行特定的处理后再显示到UI界面上,应该怎么做?答案是Future.wait,它接受一个Future数组参数,只有数组中所有Future都执行成功后,才会触发then的成功回调,只要有一个Future执行失败,就会触发错误回调。下面,我们通过模拟Future.delayed 来模拟两个数据获取的异步任务,等两个异步任务都执行成功时,将两个异步任务的结果拼接打印出来,代码如下:

    Future.wait([
      // 2秒后返回结果  
      Future.delayed(Duration(seconds: 2), () {
        return "hello";
      }),
      // 4秒后返回结果  
      Future.delayed(Duration(seconds: 4), () {
        return " world";
      })
    ]).then((results){
      print(results[0]+results[1]);
    }).catchError((e){
      print(e);
    });
    

    执行上面代码,4秒后你会在控制台中看到“hello world”。

    5)、Async/await

    如果代码中有大量异步逻辑,并且出现大量异步任务依赖其它异步任务的结果时,必然会出现Future.then回调中套回调情况。举个例子,比如现在有个需求场景是用户先登录,登录成功后会获得用户ID,然后通过用户ID,再去请求用户个人信息,获取到用户个人信息后,为了使用方便,我们需要将其缓存在本地文件系统,代码如下:

    //先分别定义各个异步任务
    Future<String> login(String userName, String pwd){
    	...
        //用户登录
    };
    Future<String> getUserInfo(String id){
    	...
        //获取用户信息 
    };
    Future saveUserInfo(String userInfo){
    	...
    	// 保存用户信息 
    }; 
    

    接下来,执行整个任务流:

    login("alice","******").then((id){
     //登录成功后通过,id获取用户信息    
     getUserInfo(id).then((userInfo){
        //获取用户信息后保存 
        saveUserInfo(userInfo).then((){
           //保存用户信息,接下来执行其它操作
            ...
        });
      });
    })
    

    消除嵌套

    login("alice","******").then((id){
      	return getUserInfo(id);
    }).then((userInfo){
        return saveUserInfo(userInfo);
    }).then((e){
       //执行接下来的操作 
    }).catchError((e){
      //错误处理  
      print(e);
    });
    

    使用 async/await 消除嵌套

    task() async {
       try{
        String id = await login("alice","******");
        String userInfo = await getUserInfo(id);
        await saveUserInfo(userInfo);
        //执行接下来的操作   
       } catch(e){
        //错误处理   
        print(e);   
       }  
    }
    
    • async用来表示函数是异步的,定义的函数会返回一个Future对象,可以使用 then 方法添加回调函数。

    • await 后面是一个Future,表示等待该异步任务完成,异步完成后才会往下走;await必须出现在 async 函数内部。

  • Stream

    Stream 也是用于接收异步事件数据,和 Future 不同的是,它可以接收多个异步操作的结果(成功或失败)。 也就是说,在执行异步任务时,可以通过多次触发成功或失败事件来传递结果数据或错误异常。 Stream 常用于会多次读取数据的异步任务场景,如网络内容下载、文件读写等。举个例子:

Stream.fromFutures([
  // 1秒后返回结果
  Future.delayed(Duration(seconds: 1), () {
    return "hello 1";
  }),
  // 抛出一个异常
  Future.delayed(Duration(seconds: 2),(){
    throw AssertionError("Error");
  }),
  // 3秒后返回结果
  Future.delayed(Duration(seconds: 3), () {
    return "hello 3";
  })
]).listen((data){
   print(data);
}, onError: (e){
   print(e.message);
},onDone: (){

});

上面的代码依次会输出:

I/flutter (17666): hello 1
I/flutter (17666): Error
I/flutter (17666): hello 3

参考链接

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多