乐趣区

关于dart:一个-JSer-的-Dart-学习日志三类

本文是“一个 JSer 的 Dart 学习日志”系列的第三篇,本系列文章次要以开掘 JS 与 Dart 异同点的形式,在温习和坚固 JS 的同时安稳地过渡到 Dart 语言。
鉴于作者尚属 Dart 初学者,所以意识可能会比拟浮浅和全面,如您慧眼识虫,心愿不吝指正。
如无非凡阐明,本文中 JS 蕴含了自 ES5 至 ES2021 的全副个性,Dart 版本则为 2.0 以上版本。

在 ES6 问世之前,宽泛风行的 JS 面向对象编程是应用 原型链 而非应用类,开发者须要对相干个性有足够的理解,并遵循一些默认的规定,能力勉强模拟出一个大抵可用的“类”。即使是 ES6 引入了 class 关键字来补救,作为新一代 JS 基础设施的类还是有待欠缺。
相比之下,Dart 对类的反对就要欠缺和弱小得多。

一. 类似的整体构造

  • 两种语言中,用于定义类的语法结构高度类似,次要包含 class 关键字、类名、包裹在花括号 {} 外部的成员。

    > /* Both JS and Dart */
    > class ClassName {
    >   attrA;
    >   attrB = 1;
    >
    >   methodA(a, b){
    >     // do something
    >     this.attrA = a;
    >     this.attrB = b;
    >   }
    > }

二. 构造函数

相同之处

  • 构造函数在实例化类的时候调用,用于解决实例化参数、初始化实例属性等;
  • 应用 super 拜访超类的构造函数;
  • 没有超类,或超类的构造函数没有参数的时候,构造函数能够省略,省略构造函数的子类实例化的时候会隐式地调用超类的构造函数。

不同之处

1. constructor vs SameName

  • JS 中的构造函数为 constructor
  • Dart 中的构造函数为 与类名统一的函数

    > /* JS */                         | /* Dart */
    > class Point{                     | class Point{>   constructor(){|   Point(){>}                              |   }
    > }                                | }

Dart 构造函数特有的性质

命名式构造函数

  • 在 Dart 中 能够为一个类申明多个命名式构造函数,来表白更明确的用意,比方将一个 Map 对象映射为一个实例:

    > class PointCanBeEncode{
    >   int x = 0;
    > 
    >    // 名为 `eval` 的命名式构造函数
    >    Point.eval(Map<String, dynamic> map){>     x = map['x'];
    >   }
    >
    >   encode(): Map<String, dynamic>{
    >     return {
    >       'x': this.x
    >     }
    >   } 
    > }

2. 属性赋值语法糖

  • 大多数状况下,构造函数的作用包含 将给定的值作为实例的属性,Dart 为此情景提供了一个非常不便的语法糖:

    > /* Dart */                              | /* Also Dart */
    > class Point {                           | class Point {>   Point(this.x, this.y);                |   Point(x, y){>}                                       |     this.x = x;
    >                                         |     this.y = y;
    >                                         |   }
    >                                         | }

    ↑ 能够看到右边的代码显著要简洁得多。

3. 初始化列表

  • Dart 能够在构造函数执行之前初始化实例变量:

    class Point {
      final double x, y, distanceFromOrigin;
    
      Point(double x, double y)
          : x = x,
          y = y,
          distanceFromOrigin = sqrt(x * x + y * y)
      {print('still good');
      }
    }

    初始化列表的执行理论甚至早于父类构造函数的执行机会。

4. 重定向构造函数

  • Dart 能够有多个构造函数,可将某个构造函数重定向到另一个构造函数:

    class Point {
      double x, y;
    
      Point(this.x, this.y);
      Point.alongXAxis(double x) : this(x, 0);
    }

    除了默认参数外没看到啥应用场景,试了一下 alongXAxis仿佛不能有函数体。。。

5. 常量构造函数

  • 如果类生成的对象都是不变的,能够在生成这些对象时就将其变为编译时常量。你能够 在类的构造函数前加上 const 关键字 确保所有实例变量均为 final 来实现该性能。

    class ImmutablePoint {
      // 所有变量均为 final
      final double x, y;
      // 构造函数为 const
      const ImmutablePoint(this.x, this.y);
    }

6. 工厂构造函数

  • JS 是一门相当灵便的语言,构造函数没有返回值的时候可视为返回新的实例,但同时构造函数也能够返回任何值作为新的实例;
  • Dart 中则能够应用 factory 关键字,来申明有返回值的构造函数。

    > /*************** 别离用两种语言实现单例模式 *****************/
    > /* JS */                       | /* Dart */
    > class A {                      | class A {
    >   static single;               |   static var single;
    >                                |   A(){}
    >   constructor() {              |   factory A.single(){>     if(!A.single) {|     if(A.single == null) {>       A.single = this;         |       A.single = A();
    >     }                          |     }
    >     return A.single;           |     return A.single;
    >   }                            |   }
    > }                              | }

工厂构造函数内不能拜访 this

7. 抽象类

  • 应用 abstruct 关键字申明抽象类,抽象类罕用于申明接口办法、有时也会有具体的办法实现。

    上面会提到形象办法,形象办法只能在抽象类中

三. 应用

相同之处

  • 均可应用 new 关键字实例化类;
  • 应用 . 拜访成员;
  • 应用 extends 关键字扩大类,并继承其属性。
> /* Both JS and Dart */
> var instance = new ClassName('propA', 42);
> instance.attrA; // 'propA'

不同之处

1. Dart 可省略 new 关键字

  • Dart 实例化类的 new 关键字能够省略,像应用一个函数那样地初始化类:

    > var instance = ClassName('propA', 42);

  • ES5 的 也是函数,省略 new 关键字的话等于执行这个函数,而 ES6 的类不再是函数,省略 new 关键字会出错。

2. Dart 命名式构造函数

  • 有了“命名式构造函数”,就能以更为灵便的形式创立一个实例,比方疾速地将一个 Map 的属性映射成一个实例:

    > var instance = PointCanBeEncode.eval({'x': 42});

    如果有存储、传输实例的需要,能够通过 实例 -> Map/List -> JSON 字符串 的计划序列化一个实例,而后通过 JSON 字符串 -> Map/List -> 新实例 的办法将其“复原”。

3. Dart 的编译时常量实例

  • 常量构造函数能够实例化编译时常量,加重运行时累赘:

    var a = const ImmutablePoint(1, 1);

  • 而 JS 基本没有编译时常量。

侧面阐明了原生类型的构造函数都是常量构造函数。

4. 重写超类的成员

  • 在 JS 中,子类的静态方法能够通过 super.xxx 拜访超类的静态方法,子类成员会笼罩同名的超类成员,但子类可在成员函数中用 super.xxx 调用超类的成员(须为非动态成员、且要先在 constructor 中调用 super);
  • Dart 中,通过 @override 注解来标名重写超类的办法(实测发现不写注解也能够,Lint 会有提醒,应该和环境设置无关)。

    > /* JS */                              | /* Dart */
    > class Super{                          | class Super{>   test(){}                            |   test(){}
    > }                                     | }
    > class Sub{                            | class Sub{
    >   /* just override it */              |   @override
    >   test(){                             |   test(){>     super.test();                     |     super.test()';
    >   }                                   |   }
    > }                                     | }      

Dart 的 mixin

  • 在 Dart 中申明类的时候,用 with 关键字来混入一个没有 constructor 的类,该类可由mixin 关键字申明:

    mixin Position{
      top: 0;
      left: 0;
    }
    
    class Side with Position{}

四. 成员属性和成员办法

相同之处

  • 成员函数外部应用 this 拜访以后实例,应用点号 (.) 拜访成员;
  • 定义 gettersetter

    > /* JS */                         | /* Dart */
    > class Test {                     | class Test {
    >     #a = 0;                      |   private a = 0;
    >     get b(){                     |   get b(){
    >         return this.#a;          |       return this.a;
    >     }                            |   }
    >     set b(val){|   set b(val){
    >         this.#a = a;             |       this.a = val;
    >     }                            |    }
    > }                                | }
  • static 定义动态变量 / 属性。

不同之处

1. 类的闭合作用域

  • JS 的类没有作用域,因而拜访成员必须用 this
  • Dart 的类有笔和作用域,在成员函数中能够间接拜访其余成员,而不用指明this
> /* JS */                         | /* Dart */
> const a = 3;                     | const a = 3;
> class Test{                      | class Test{
>   a = 1;                         |   a = 1;
>   test(){                        |   test(){>     console.log(a === this.a);   |     print('${a == this.a}');
>     /* 'false' */                |     // 'true'
>   }                              |   }
> }                                | }

2. 公有变量 / 属性

  • JS 中类实例的公有成员应用 # 前缀申明,拜访时也要带上此前缀;
  • Dart 中实例的公有成员应用 _ 前缀申明,在“类作用域”中间接能够拜访。
> /* JS */                        | /* Dart */
> class Test{                     | class Test{
>   #secret = 1234;               |   _secret = 1234;
>   test(){                       |   test(){>     console.log(this.#secret);  |     print('${_secret}');
>   }                             |   }
> }                               | }

JS 的公有成员是一个很“年老”的属性,在此之前,应用下划线命名公有成员是一个被 JS 社区宽泛承受的约定。
ES 最终没有钦定 _ 作为公有成员的申明计划,也没有采纳 JavaTS 中应用的 private,而是采纳了 # 号语法。

TypeScript:TC39 老哥,你这样让我很难办诶!

3. 操作符重载

JS 并不反对操作符重载,所以在某些场景中咱们须要自定义一些办法来进行实例之间的运算,比方多个向量相加可能会写成:
const d = a.add(b).add(c)

  • Dart 反对重载操作符,开发者能够为实例间的运算定制逻辑,比方向量运算:

    class Vector{
      final double x, y;
    
      const Vector(this.x, this.y);
    
      Vector operator +(Vector obj) => Vector(obj.x + x, obj.y + y);
    }

    向量相加就能够写作const c = a + b + c

    凭借操作符重载能够定义一些十分直观的语法,例如应用 &&|| 求汇合与图形的交、并集。

4. 形象办法

  • Dart 中的实例办法、Getter 办法以及 Setter 办法都能够是形象的,定义一个接口办法而不 去做具体的实现让实现它的类去实现该办法 ,形象办法 只能存在于抽象类中
退出移动版