美文网首页
Dart 语法 学习笔记

Dart 语法 学习笔记

作者: SingleDigit | 来源:发表于2019-11-14 16:59 被阅读0次

    语法学习笔记。资料参考:官方文档(英文)
    (要直接看英文文档,中文文档可能是机器翻译的,很多地方语句不通顺,埋坑无数)


    语言的特性

    1. 所有能放进变量中的都是对象,所有对象都是类的实例,所有对象都继承于Object。包含数字,方法,null等等。也由此可以知道:没有初始化的变量都是null;没有装箱拆箱的问题。
    2. Dart是强类型语言。你可以显示声明类型,也可以直接使用var,Dart会自动推断类型。当你要声明一个不确定类型的变量时,可以使用特殊类型dynamic来声明。
    3. 支持顶级方法,类方法,嵌套方法(在方法中声明方法)
    4. 没有其他语言的public等关键字,但如果一个标识符以 "_" 开头,则为私有。
    5. new关键字是可选的。

    内置类型

    1. numbers:带小数点的就是double,不带的就是int

    2. Strings:
      1.创建既可以用""也可以用''
      2.编码是UTF-16;
      3.可以使用"${xxx}"来格式化输出字符串,其中的{}一般情况下可以省略,eg."a = $a" "a = ${a}." "a = ${a}ah",其中最后一个需要加{}是因为如果不加{},相当于"a = $aah",使用的值是aah,而不是a

      1. 多行字符串''' xxxx ''' 或者 """ xxx """
      2. r'\n' 输出的是 \n。其中r取消字符串中的转义字符。
    3. Booleans:不允许使用if(0)之类的代码,会编译报错。

    4. Lists:

      1. 索引从0开始
      2. 使用const创建的list,该变list中的单个元素会运行报错。
        var list = const[1, 2, 3, 4]; list[0] = 12;//这里报错
      3. 可以在list创建时使用iffor
          bool isAddExtraElement = false;
          var list = [
            0,
            1,
            if(isAddExtraElement) 2
          ];
          print(list.toString());
        
          var list2 = [
            for(int i=0; i < list.length; i++)
              "forElement:${list[i]}"
          ];
          print(list2);
        
    5. Sets:无序列表

      1. 创建
          //var test = {}; //这样声明的是map,不是set
          var test = <String>{};
          var test1 = {"a", "b", "c", "d", "e"};
          var test2 = <String>{};
          test2.add("f");
          test2.addAll(test1);
        
      2. 一样可以用const进行创建,使用const创建的set不能更改。
    6. Maps:字典

      1. 创建
        var map1 = {
          1: 15,
          2: 16,
          3: 17,
          4: 18
        };
        map1[5] = 19;
        
        var map2 = Map();
        map2["q"] = 1;
        map2["w"] = 2;
        map2["e"] = 3;
        
      2. 可以使用const进行创建,使用const创建的Map不能更改。
    7. Runes:使用4位16进制数可以输出一些特殊字符。但一般好像没什么卵用。

      Runes input = new Runes(
          '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
      print(new String.fromCharCodes(input));
    

    输出: ♥ 😅 😎 👻 🖖 👍

    1. Symbols:不清楚这个东西是干嘛的,好像也没什么卵用。

    函数

    1. 函数的返回值类型可以省略,下面两个函数是一样的。
        add(num a, num b) {
          return a + b;
        }
    
        num add(num a, num b) {
          return a + b;
        }
    
    1. 不定参数
    ```
    add(num a, num b, [num c = 0, num d]) {//[]中是不定参数,其中c带默认值
      return a + b + c + (d ?? 0);
    }
    
    sub({num a = 0, num b = 0, num c}){//{}中是不定参数,其中a, b带默认值
      return a - b - (c ?? 0);
    }
    
    main(List<String> arguments) {
      print(add(1, 2, 3));//和其他语言使用方法一致
      print(sub(b : 2, a : 1));//传入参数是按照key值对应的,不用考虑顺序。&&个人认为这种方式使用有些麻烦。
    }
    ```
    
    1. 函数能够直接当做变量进行传递,类似于C#中的Action,但不用额外进行变量的定义。
    2. 匿名函数,使用lamda表达式,语法与C#基本一致。
    3. 值的一看的官方闭包的例子,用函数创建函数。
    /// Returns a function that adds [addBy] to the
    /// function's argument.
    Function makeAdder(num addBy) {
      return (num i) => addBy + i;
    }
    
    void main() {
      // Create a function that adds 2.
      var add2 = makeAdder(2);
    
      // Create a function that adds 4.
      var add4 = makeAdder(4);
    
      assert(add2(3) == 5);
      assert(add4(3) == 7);
    }
    

    关于++ii++

    和其他语言的一样。
    这里只是简单记录一下,顺便复习,&&知道这么个东西就行了,开发中不要用,不差多一行代码。
    话说谁要是在写代码的时候抖这个机灵,不出bug一切安好,一旦出bug可能让你查到天荒地老。

    官方例子

    var a, b;
    
    a = 0;
    b = ++a; // Increment a before b gets its value.
    assert(a == b); // 1 == 1
    
    a = 0;
    b = a++; // Increment a AFTER b gets its value.
    assert(a != b); // 1 != 0
    
    a = 0;
    b = --a; // Decrement a before b gets its value.
    assert(a == b); // -1 == -1
    
    a = 0;
    b = a--; // Decrement a AFTER b gets its value.
    assert(a != b); // -1 != 0
    

    总之:a靠近= ==> 先赋值

    流程控制

    1. if(xxx){} else if(xxx) {} else{}
    2. for(int i = 0; i < xxx.Length; i++) && for(var a in xxx)for ... in 的用法基本等同于C#中的foreach
    3. while(xxx){} && do{}while{xxx}
    4. switch case 基本用法与C#一样,但有一个额外的用法
    var command = 'CLOSED';
    switch (command) {
      case 'CLOSED':
        executeClosed();
        continue nowClosed;//强制继续执行下面的case。个别地方可能会省一些代码。
      // Continues executing at the nowClosed label.
    
      nowClosed:
      case 'NOW_CLOSED':
        // Runs for both CLOSED and NOW_CLOSED.
        executeNowClosed();
        break;
    }
    
    1. assert false时中断程序,抛出异常。

    抛出异常

    throw 'something error !!!',可以直接写异常信息。(当然也可以指定异常类型)

    异常捕获

    try...catch...finally
    用法基本与C#的相同,但个别的语法不一样

    try {
      breedMoreLlamas();
    } on OutOfLlamasException {
      // A specific exception
      buyMoreLlamas();
    } on Exception catch (e) {
      // Anything else that is an exception
      print('Unknown exception: $e');
    } catch (e, s) {
      // No specified type, handles all
      print('Something really unknown: $e  $s');
    } finally {
      // Always clean up, even if an exception is thrown.
      cleanLlamaStalls();
    }
    

    一般类的定义和使用和C#基本一致。需要注意下没有public等关键字就好了。下面记一下有区别的地方。

    1. 构造函数
      1. 纯类名构造函数只允许有一个。默认为ClassName()。父类额外定义的构造函数,子类至少要继承声明一个。
      2. 特有的构造函数声明方法
      class Point {
      num x, y;
      
        Point(this.x, this.y);//省略赋值。这种方式的构造函数只能有一个。
        Point.origin() {//命名构造函数,使用时调用Point.origin()即可。多个构造函数可以用此定义。
           x = 0;
           y = 0;
       }
      }
      
      1. 构造函数不能自动继承,需要手动实现。
      class Animal {
        num age;
        Animal(this.age);//定义初始构造函数,唯一
        Animal.normal()  :  this(0);//使用默认构造函数
      
        Animal.dead() {
          age = 9999;
        }
      }
      
      class Dog extends Animal {
         Dog(num age) : super(age);//至少实现一个
      }
      
      1. initializer list:冒号后面的部分。构造时优先执行。可以在这里进行一些数据的初始处理或者判断。多条使用 , 隔开。
        class Point{
          num x;
          num y;
      
          Point(num x, num y) : assert(x != 0 || y != 0) {//数据错误检查
            this.x = x;
            this.y = y;
          }
      
          Point.original() : x = 0, y = 0 {//数据初始值赋值
              print("create default point");
          }
        }
      
      1. 创建对象时,构造函数的执行顺序。initializer list-->父类构造函数-->子类构造函数
      2. Const构造函数。要求使用时需要类中有final量。
      class Point{
        final num x;
        final num y;
        const Point(this.x, this.y);
        const Point.test(this.x, this.y);
      }
      
      main(List<String> arguments) {
        var point = Point(15, 16);
        var point2 = Point(15, 16);
        print(point == point2);//false
      
        var point3 = const Point(1, 1);
        var point4 = const Point(1, 1);
        print(point3 == point4);//true
      
        var point5 = const Point(1, 2);
        var point6 = const Point(1, 3);
        print(point5 == point6);//false
      
        var point7 = const Point.test(1, 2);
        var point8 = const Point(1, 2);
        print(point7 == point8);//true
      }
      
      1. factory构造函数
        class Message {
          String content;
      
          static final Map<String, Message> _map = {};//静态缓存
      
          factory Message(String content) {
            return _map.putIfAbsent(content, ()=>Message._internal(content));//找不到则执行该方法进行添加
          }
      
          Message._internal(this.content);//内部调用的构造函数
        }
      
        main(List<String> arguments) {
          var message1 = Message("asd");
          var message2 = Message("asd");
          print(message1 == message2);//true
      
          var message3 = Message("asd3");
          print(message1 == message3);//false
        }
      
    2. 属性
    class Message {
      String _content = "";
    
      get content => _content;
      set content(String value) => _content = value;
    
      get content2 { return _content; }
      set content2(String value) { _content = value; }
    }
    
    main(List<String> arguments) {
      var message = Message();
      print(message.content);
      print(message.content2);
      message.content = "hello world";
      print(message.content);
      message.content2 = "hello world2";
      print(message.content2);
    }
    
    1. 抽象类:关键字abstract
      与C#一样,只有抽象类中才能有抽象属性,方法。
    abstract class Message {
      String _content = "";
      get content;
    
      func();
    
      func2(){//有实现的不算抽象方法
        return 16;
      }
    }
    
    class Message2 extends Message{
      @override
      func() {
        // TODO: implement func
        return null;
      }
    
      @override
      // TODO: implement content
      get content => null;
    }
    
    1. 类的继承
      使用extends关键字。子类中重新实现了父类的方法的话默认重载。如果真的需要重载的话,记得最好加上@overrider。另外,需要的话记得加上super.xxx()来调用父类的方法。下面是例子:
    class Add{
      num add(num a, num b){
        return a + b;
      }
    
      test(){}
    }
    
    class SubAdd extends Add{
      @override
      num add(num a, num b){
        super.test();
        super.add(a, b);
        return a * b;
      }
    }
    
    main(List<String> arguments) {
      var test = SubAdd();
      print(test.add(10, 20));
    }
    
    1. Implicit接口
      Dart中没有像C#中的Interface的关键字,但Dart的每一个类都相当于定义了一个隐式的接口。如果ClassA想实现ClassB的功能,那么ClassA需要implementsClassB。下面是例子:
    class Add{
      num add(num a, num b){
        return a + b;
      }
    
      num other(num a, num b){
        return (a + b) / (a - b);
      }
    }
    
    class Sub{
      num sub(num a, num b){
        return a - b;
      }
    }
    
    class Calculation implements Add, Sub{//implements后面的类,仅仅作为接口,接口需要自己额外进行实现,和原来的类里有没有实现没关系
      @override
      num add(num a, num b) {
        return a + b;
      }
    
      @override
      num other(num a, num b) {
        return a * b;
      }
    
      @override
      num sub(num a, num b) {
        return a - b;
      }
    }
    
    main(List<String> arguments) {
      var calculation = Calculation();
    
      print(calculation.add(10, 20));
      print(calculation.other(10, 20));
      print(calculation.sub(10, 20));
    }
    
    1. Static:用法和C#一样,但有些需要注意的点。
      1. 类中的静态变量,如果从没有被使用过,它是不初始化的。
      2. 类中的静态方法,如果可以的话,推荐将静态方法改成顶级方法。

    枚举类型

    与C#基本一样

    enum Color {
      Red,//不能像C#一样额外定义枚举的Index值
      Green,
      Blue,
      Yellow,
    }
    
    main(List<String> arguments) {
      var colors = Color.values;//转成list,可以用index来取
      for(var a in colors){
        print(a);
      }
    }
    

    Mixins

    我额外参考的一篇文章,说明的很详细具体,推荐阅读:【译】Dart | 什么是Mixin
    几个重要的关键字:mixin on with,相关的内容在下面的例子里注释说明。
    觉得这个功能在有些时候会很有用,但现在自己还没有实际操作过,理解不够,只能先记下来,留着以后再补充了。

    class DoA{
      factory DoA._(){//这里通过工厂方式,禁止外部创建。当然也可以根据需求允许创建
        return null;
      }
    
      void canDoSomethingA(){
        print("do A");
      }
    }
    
    mixin DoB{//mixin关键字,使该类没有构造器,无法被创建。
      void canDoSomethingB(){
        print("do B");
      }
    }
    
    mixin DoC{
      void canDoSomethingC(){
        print("do C");
      }
    }
    
    mixin DoD on Person {//mixin...on... ,相当于 class ... extends ...
      void canDoSomethingD(){
        print("do D");
      }
    }
    
    class Person {
      void Say(String content){
        print("Say : $content");
      }
    }
    
    class Person1 extends Person with DoA, DoB {}
    class Person2 extends Person with DoB, DoC {}
    class Person3 extends Person with DoC, DoD {}
    
    main(List<String> arguments) {
      var person1 = Person1();
      print(person1);
      person1.canDoSomethingA();
      person1.canDoSomethingB();
      print(person1 is Person);//true
      print(person1 is DoA);//true
      print(person1 is DoB);//true
    
      var person2 = Person2();
      print(person2);
      person2.canDoSomethingB();
      person2.canDoSomethingC();
    
      var person3 = Person3();
      print(person3);
      person3.canDoSomethingC();
      person3.canDoSomethingD();
      person3.Say("Hello World");
    }
    

    泛型

    和C#中的泛型用法相同,但是约束语法不一样,也不能像C#一样有多个约束条件(因为Dart中没有接口)。
    下面是例子:

    class Person {}
    
    class Test<T extends Person>{
      void Record(){
        print(T);
      }
    
      K DoSomething<K extends num>(K value){
        print("$K  $value");
        return value * 2;
      }
    }
    
    main(List<String> arguments) {
      var test = Test<Person>();
      test.Record();
      print(test.DoSomething<int>(10));
      print(test.DoSomething<double>(10.11));
    }
    

    Libraries

    通俗的讲,就是通过导入的方式,来使用其他脚本里的方法,类,变量等等。

    1. import 'xxxx' [as xxx] [show aaa[, aaa2, aaa3]] [hide bbb[, bbb1, bbb2, bbb3]];方括号中表示可选。
      其中:as表示定义lib使用时前缀,show表示将lib中指定的方法、类进行导出, hide表示将指定的方法、类进行隐藏。
    2. deferred as,表示使用时再导入(Flutter 不支持)。
    import 'package:dart_demo/dart_demo.dart' deferred as dart_demo;
    
    Future load(Function action) async{
      await dart_demo.loadLibrary();
      action();
    }
    
    main(List<String> arguments) {
      load(()=>print(dart_demo.calculate1()));
    }
    

    异步

    1. 一般异步:
      关键字 async await
      loadRes(String path) async{
        print(path);
      }
      
      initRes() async{
        await loadRes("path1");
        await loadRes("path2");
        await loadRes("path3");
      }
      
      main(List<String> arguments) {
        var future = initRes();
        future.whenComplete(()=>print("finished"));
      }
      

    2. 其实和上面的一样,就是await在流里面有个特殊用法。
      read() async {
        try {
          var path = r"C:\Users\Admin\Desktop\test.txt";
          var file = File(path);
          var stream = file.openRead();
          await for(var a in stream) { //等待流读取
            print(a);
          }
        } catch(e) {
          print(e);
        }
      }
      
      main(List<String> arguments) {
        read();
      }
      

    其他的零碎东西

    需要额外记的关键字
    1. final 相当于 C#中的 readonly。可以直接用final进行变量的声明,而不用额外去指定变量类型。eg. final a = 15;
    2. const,除了其他一般语言的编译时常量的用法以外,还可以用来创建不可改变的值。eg. var list = const [];
    3. is!obj is! TypeA 如果obj不是TypeA类型则返回true
    4. ..:一个语法糖,让你能够对一个对象进行连续的一些列操作,而不用额外去写对象的变量名称。下面是官方例子,两组表达式进行的操作是完全相同的。支持嵌套(这个个人不推荐,嵌套太多容易出坑)。
      querySelector('#confirm') // Get an object.
        ..text = 'Confirm' // Use its members.
        ..classes.add('important')
        ..onClick.listen((e) => window.alert('Confirmed!'));
      
      var button = querySelector('#confirm');
      button.text = 'Confirm';
      button.classes.add('important');
      button.onClick.listen((e) => window.alert('Confirmed!'));
      
    5. ?=b ?= 5;,如果bnull,则将b赋值为5,如果b不是null,则b保持原样。
    6. ~/:整除
    7. ~:按位取反
    Generators

    关键字:sync* async* yield yield*

    1. 同步的返回Iterable<T>,类似于C#中的IEnumerable<T>
    Iterable<int> test() sync* {//sync* 标记当前函数为Generators函数,返回Iterable<T> 类型
      for(int i=0;i<100;i++) {
        yield i;//表示将什么值放进Iterable<T>里面
      }
    }
    
    1. 异步的返回Stream<T>
    Stream<int> test(int startNum) async* { //async*标记函数为异步的Generators,返回Stream<T>
      if(startNum < 10) {
        yield startNum;
        yield* test(startNum + 1); //递归要用yield*
      }
    }
    
    main(List<String> arguments) async {
      var st = test(1);
      await for(var a in st) {
        print(a);
      }
    }
    
    Callable classes

    可以像使用函数一样使用这种类的对象。

    class Test {
      String content;
      Test(this.content);
    
      call(String other) => "$other $content";//实现call方法后,示例的对象可以作为函数对象来使用。可以有传参,也可以没有
    }
    
    main(List<String> arguments) async {
      var t = Test("hello world");
      print(t("Tina"));//使用执行函数的方式,调用类中的call()方法
    }
    
    Isolates

    目前的简单理解是多线程,不一定正确&需要进行实践。等后面遇到了,理解了再将笔记补上。

    Typedefs

    目前的理解,是给函数的类型定一个别名。感觉形式上有点像C#里面的委托。

    typedef num Calculate(num a, num b);//将这种类型的函数定义为Calculate类型的函数
    
    num add(num x, num y) => x + y;
    sub(num x, num y) => x - y;
    
    main(List<String> arguments) async {
      print(add is Calculate);//true
      print(sub is Calculate);//false
    }
    
    Metadata
    1. 使用
      Metadata能够在类,变量,参数,函数等等的前面使用。运行时,可以通过反射获取到Metadata。
    class Television {
      /// _Deprecated: Use [turnOn] instead._
      @deprecated//表示这个函数方法被弃用
      void activate() {
        turnOn();
      }
    
      /// Turns the TV's power on.
      void turnOn() {...}
    }
    
    1. 创建
      官方例子
    library todo;
    
    class Todo {
      final String who;
      final String what;
    
      const Todo(this.who, this.what);
    }
    
    import 'todo.dart';
    
    @Todo('seth', 'make this do something')
    void doSomething() {
      print('do something');
    }
    
    注释

    需要说的就是文档注释,其他的和C#一样。
    方括号中表示变量

      /// Exercises your llama with an [activity] for
      /// [timeLimit] minutes.
      void exercise(Activity activity, int timeLimit) {
        // ...
      }
    

    官方文档,基础的部分算是看完了。后面实际操作中发现有问题会进行补充和修改。
    如果有人看的话,发现错误欢迎指正。
    另外发现ide的自动格式化有些不好用,有好用的ide也欢迎推荐。

    相关文章

      网友评论

          本文标题:Dart 语法 学习笔记

          本文链接:https://www.haomeiwen.com/subject/aospbctx.html