1
头图

This article is the third in the series of "A JSer's Dart Learning Log". This series of articles mainly aims to explore the similarities and differences between JS and Dart, while reviewing and consolidating JS while smoothly transitioning to the Dart language.
Since the author is still a beginner to Dart, his understanding may be superficial and one-sided. If you know the worms, I hope you can correct me.
Unless otherwise specified, the JS in this article contains all the features from ES5 to ES2021, and the Dart version is 2.0 or higher.

Before ES6 came out, the widely popular JS object-oriented programming used prototype chain instead of using classes. Developers need to have sufficient understanding of relevant features and follow some default rules to barely simulate a generally usable " kind". Even if ES6 introduces the class keyword to make up for it, it still needs to be improved as a new generation of JS infrastructure.
In contrast, Dart's support for classes is much more complete and powerful.

1. Similar overall structure

  • In the two languages, the grammatical structures used to define classes are highly similar, mainly including class , class names, and members {}

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

2. Constructor

Similarities

  • The constructor is called when the class is instantiated, and is used to process instantiation parameters, initialize instance attributes, etc.;
  • Use super access the superclass's constructor;
  • When there is no superclass or the constructor of the superclass has no parameters, the constructor can be omitted. When the subclass of the omitted constructor is instantiated, the superclass's constructor will be implicitly called.

the difference

1. constructor vs SameName

  • The constructor in JS is constructor ;
  • , which is the same function as the class name .
> /* JS */                         | /* Dart */
> class Point{                     | class Point{
>   constructor(){                 |   Point(){
>   }                              |   }
> }                                | }

Specific properties of Dart constructor

1. Named constructor

  • In Dart you can declare multiple named constructors for a class to express a clearer intent , such as Map object to an instance:

    > class PointCanBeEncode{
    >   int x = 0;
    > 
    >    // 名为 `eval` 的命名式构造函数
    >    Point.eval(Map<String, dynamic> map){
    >     x = map['x'];
    >   }
    >
    >   encode(): Map<String, dynamic>{
    >     return {
    >       'x': this.x
    >     }
    >   } 
    > }
    Of course, since JS is a very flexible language, we can also JS the static method of Dart into a named constructor similar to 06164057625d1b.

2. Syntax sugar for attribute assignment

  • In most cases, the role of the constructor includes given value as an instance attribute . Dart provides a very convenient syntactic sugar for this situation:

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

    ↑ You can see that the code on the left is obviously much more concise.

3. Initialization list

  • Dart can initialize instance variables before the constructor is executed:

    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');
      }
    }
    The execution of the initialization list is actually even earlier than the execution timing of the parent class constructor.

4. Redirection constructor

  • Dart can have multiple constructors, and you can redirect one constructor to another:

    class Point {
      double x, y;
    
      Point(this.x, this.y);
      Point.alongXAxis(double x) : this(x, 0);
    }
    Except for the default parameters, I didn't see any usage scenarios. I tried alongXAxis it seems that there is no function body. . .

5. Constant Constructor

  • If the objects generated by the class are constant, you can change them to compile-time constants when generating these objects. You can in front of the class's constructor plus const keyword and ensure that all instance variables are final to implement this function.

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

6. Factory Constructor

  • JS is a very flexible language. When the constructor does not return a value, it can be regarded as returning a new instance, but at the same time the constructor can also return any value as a new instance;
  • In Dart, you can use the factory keyword to declare a constructor with a return value.
> /*************** 分别用两种语言实现单例模式 *****************/
> /* JS */                       | /* Dart */
> class A {                      | class A {
>   static single;               |   static var single;
>                                |   A();
>   constructor() {              |   factory A.single(){
>     if(!A.single) {            |     if(single == null) {
>       A.single = this;         |       single = A();
>     }                          |     }
>     return A.single;           |     return single;
>   }                            |   }
> }                              | }
> var instance = new A();        | var instance = A.single();
this cannot be accessed within the factory constructor.

7. Abstract class

  • Use the abstruct keyword to declare abstract classes. Abstract classes are often used to declare interface methods and sometimes have specific method implementations.
The abstract method will be mentioned below. The abstract method can only be used in the abstract class .

3. Use

Similarities

  • You can use the new keyword to instantiate the class;
  • Use . visit members;
  • Use the extends keyword to extend the class and inherit its properties.
> /* Both JS and Dart */
> var instance = new ClassName('propA', 42);
> instance.attrA; // 'propA'

the difference

1. Dart can omit the keyword new

  • new keyword of the Dart instantiation class can be omitted, and the class can be initialized like a function:

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

  • The class of ES5 is also a function. Omitting the new keyword is equivalent to executing this function, while the ES6 class is no longer a function. Omitting the new keyword will cause an error.

2. Dart named constructor

  • With the "named constructor", an instance can be created in a more flexible way, such as quickly mapping an attribute of Map

    > var instance = PointCanBeEncode.eval({'x': 42});
If you need to store and transfer the instance, you can instance through the 161640576261f8 instance -> Map/List -> JSON string solution, and then use the JSON string -> Map/List -> new instance method to serialize it. recover".

3. Dart's compile-time constant instance

  • The constant constructor can instantiate compile-time constants, reducing the burden at runtime:

    var a = const ImmutablePoint(1, 1);

  • And JS has no compile-time constants at all.
The side shows that the constructors of primitive types are constant constructors.

4. Override the members of the superclass

  • In JS, the static methods of the subclass can super.xxx members of the subclass will override the members of the superclass with the same name, but the subclass can call the members of the superclass super.xxx members, and the first in constructor call super );
  • In Dart , use the @override annotation to mark the method of rewriting the superclass (the actual test found that you can compile without writing the annotations, Lint will give a prompt, which should be related to the environment configuration).

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

Dart's mixin

  • When declaring a class in Dart, use the with keyword to mix in a constructor , which can be mixin keyword:

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

Four. Member attributes and member methods

Similarities

  • Inside the member function, use this access the current instance, and use the dot ( . ) to access the members;
  • Use static keywords to define static members;
  • Definition getter and setter :

    > /* 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;
    >     }                            |    }
    > }                                | }

the difference

1. The closed scope of the class

  • The JS class has no scope, so access members must use this ;
  • Dart's class has a closed scope, and other members can be directly accessed in member functions without specifying 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. Private variables/attributes

  • The private members of the class instance in JS are # , and this prefix should also be used when accessing;
  • The private members of the instance in Dart are _ can be accessed directly in the "class scope".
> /* JS */                        | /* Dart */
> class Test{                     | class Test{
>   #secret = 1234;               |   _secret = 1234;
>   test(){                       |   test(){
>     console.log(this.#secret);  |     print('${_secret}');
>   }                             |   }
> }                               | }
Private members of JS are a very "young" attribute. Prior to this, naming private members with underscores was a widely accepted convention by the JS community.
ES ultimately did not handpicked _ as a private member's statement scheme, we do not adopt Java and TS used private , instead of using # number syntax.
In Dart, the # syntax is used for the literal value of Symbol

3. Operator overloading

  • Dart supports overloaded operators, and developers can customize logic for operations between instances, such as vector operations:

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

    The addition of vectors can be written as const c = a + b + c .

With operator overloading, some very intuitive grammars can be defined. For example, use && and || find the intersection and union of a set and a graph.

JS does not support operator overloading, so in a vector operation scenario similar to the above, we need to customize some methods to perform operations between instances. For example, the addition of multiple vectors may be written as:

  • const d = a.add(b).add(c)
  • const d = Vector.add(a, b, c)

4. Abstract methods

  • The instance method of the abstract class Getter method, and the Setter method in Dart can all be abstract, define an interface method and do not do the specific implementation, let the class that implements it implement the method , the abstract method can only Exists in the abstract class .

5. call method and callable class instance

  • Only functions can be called in JS. When we expect to implement "stateful" functions (such as incremental ID generators), we usually use function external variables or functions to return inner functions (using the nature of closures);
  • In Dart, call method is deployed, and its instance can be called as a function, and the call method of the instance is called:

    > /*  JS  */                               | // Dart
    > const IdGen = () => {                    | class IdGen {
    >   let seed = 0;                          |   int _seed = 0;
    >   return () => {                         |   call(){
    >     return ++seed;                       |     return ++_seed;
    >   }                                      |   }
    > };                                       | }
    > const idGen = IdGen();                   | final idGen = IdGen();
    > console.log(idGen());                    | print(idGen());
    > // 1                                     | // 1


知名喷子
6k 声望2.5k 粉丝

话糙码不糙。