Dart Tips

作者: Inlight先森 | 来源:发表于2019-12-30 18:07 被阅读0次

    DartPad

    DartPad的出现让人眼前一亮,以后可以随时随地开心的测试一些代码了,Dart的大部分语言功能DartPad都支持。打开DartPad

    变量初始化

    如果一个对象的引用不局限于单一的类型,可以根据设计指南将其指定为Objectdynamic类型:

    dynamic name1 = 'Jack';
    Object name2 = 'Rose';
    

    如果一个对象限于单一的类型:

    var name1 = 'Jack';
    String name2 = 'Rose';
    

    局部变量声明的best practice是使用var来做类型被推断而非使用指定的类型。这样使用可以和其他大部分语言保持一致,提高可读性。

    扩展操作符 ......?

    将一个 List 中的所有元素插入到另一个 List 中:

    var list1 = [1, 2, 3];
    var list2 = [0, ...list1];
    assert(list2.length == 4);
    

    如果扩展操作符右边可能为null:

    var list1;
    var list2 = [0, ...?list1];
    assert(list2.length == 1);
    

    集合中使用控制流

    使用Collection If来创建一个List:

    var animals = [
      'Dog',
      'Cat',
      'Fish',
      if (canFly) 'Bird'
    ];
    

    使用Collection For将列表中的元素修改后添加到另一个列表中:

    var listOfInts = [1, 2, 3];
    var listOfStrings = [
      0,
      for (var i in listOfInts) i*i
    ];
    print(listOfStrings); // [0, 1, 4, 9]
    

    常量声明 final 和 const

    • const是编译时常量,final是运行时常量。二者都只能被赋值一次。
    • 实例变量可以是final的但不可以是const的,final实例变量必须在构造器开始前被初始化,比如在声明实例变量时初始化,或者作为构造器参数,或者将其置于构造器的初始化列表中。
    • 如果使用const修饰类中的变量,则必须加上static关键字,即static const
    • 没有使用finalconst修饰的变量的值是可以被更改的,即使这些变量之前引用过const的值。
    var list = const [];
    list = [1, 2, 3];
    
    • Dart2.5之后可以在定义常量时使用isas......?collection ifcollection for
    const Object i = 3;
    const list = [i as int];
    const map = {if (i is int) i: "int"};
    const set = {if (list is List<int>) ...list};
    

    可选参数

    • 命名参数:使用{param1, param2, …}的形式来指定命名参数。调用时需要指定对应的命名,也可以使用@required来标识一个命名参数是必须的参数。使用@required需要导入package:meta/meta.dart
      void setUserInfo(String name, {@required int age, double height = 0, double weight = 0}) {
        print('My name is $name, I am $age years old.');
        if (height > 0) { print('My height is $height'); }
        if (weight > 0) { print('My weight is $weight'); }
        print('\n');
      }
      setUserInfo('Jack', age: 18);
      setUserInfo('Rose', age: 18, height: 170);
    
    My name is Jack, I am 18 years old.
    
    My name is Rose, I am 18 years old.
    My height is 170
    
    • 位置参数:使用[param1, param2, …]的形式来指定位置参数。调用时需将位置对应起来。
      void setUserInfo(String name, [int age, double height = 0, double weight = 0]) {
        print('My name is $name, I am $age years old.');
        if (height > 0) { print('My height is $height'); }
        if (weight > 0) { print('My weight is $weight'); }
        print('\n');
      }
      setUserInfo('Jack', 18);
      setUserInfo('Rose', 18, 170, 105);
    
    My name is Jack, I am 18 years old.
    
    My name is Rose, I am 18 years old.
    My height is 170
    My weight is 105
    

    级联调用

    ..级联操作并非一个运算符而是Dart的特殊语法。

    querySelector('#confirm') // 获取对象
      ..text = 'Confirm' // 使用对象的成员
      ..classes.add('important')
      ..onClick.listen((e) => window.alert('Confirmed!'));
    

    querySelector返回了一个Selector对象,后面的级联操作符都是调用这个Selector对象的成员并忽略每个操作的返回值。上面的代码等价于:

    var button = querySelector('#confirm');
    button.text = 'Confirm';
    button.classes.add('important');
    button.onClick.listen((e) => window.alert('Confirmed!'));
    

    在返回对象的函数中谨慎使用级联操作符:

    var sb = StringBuffer();
    sb.write('foo')
      ..write('bar'); // (Error: method 'write' isn't defined for 'void').
    

    上述代码中的 sb.write() 方法返回的是 void,返回值为 void 的方法则不能使用级联运算符。

    Getter 和 Setter

    所有实例变量均会隐式地声明一个Getter方法,非final类型的实例变量还会隐式地声明一个Setter方法。可以使用getset关键字为额外的属性添加GetterSetter方法:

    class Rectangle {
      num left, top, width, height;
      Rectangle(this.left, this.top, this.width, this.height);
      // 定义两个计算产生的属性:right 和 bottom。
      num get right => left + width;
      set right(num value) => left = value - width;
      num get bottom => top + height;
      set bottom(num value) => top = value - height;
    }
    void main() {
      var rect = Rectangle(3, 4, 20, 15);
      assert(rect.left == 3);
      rect.right = 12;
      assert(rect.left == -8);
    }
    

    使用 Getter 和 Setter 的好处是,你可以先使用你的实例变量,过一段时间过再将它们包裹成方法且不需要改动任何代码,即先定义后更改且不影响原有逻辑。

    类中的关键字

    • abstract可以让该类成为抽象类,抽象类将无法被实例化,常用于声明接口方法。
    abstract class Animal {
      void eat(); // 抽象方法。
    }
    class Dog extends Animal {
      void eat() {
        print('Dog eat!'); 
      }
    }
    
    • implements为隐式接口,每一个类都隐式地定义了一个接口并实现了该接口,这个接口包含所有这个类的实例成员以及这个类所实现的其它接口。如果想要创建一个A类支持调用B类的API且不想继承B类,则可以实现B类的接口。
    class Point implements Comparable, Location {...}
    
    • extends关键字创建一个子类,可使用super关键字引用一个父类,可以使用@override重写父类的实例方法。
    class Television {
      void turnOn() {
      }
    }
    class SmartTelevision extends Television {
      void turnOn() {
        super.turnOn();
      }
    }
    
    class SmartTelevision extends Television {
      @override
      void turnOn() {
      }
    }
    
    • Mixin是一种在多重继承中复用某个类中代码的方法模式。使用with关键字在其后跟上Mixin类的名字来使用Mixin模式。Mixin类继承自Object并且不为该类定义构造函数,可以使用关键字on来指定哪些类可以使用该Mixin类。
    class Musician extends Performer with Musical {
    }
    class Maestro extends Person with Musical, Aggressive, Demented {
    }
    
    mixin MusicalPerformer on Musician {
    }
    

    库和可见性

    • as用来指定前缀解决两个代码库有冲突的情况。比如如果library1library2都有Element类:
    import 'package:lib1/lib1.dart';
    import 'package:lib2/lib2.dart' as lib2;
    // 使用 lib1 的 Element 类。
    Element element1 = Element();
    // 使用 lib2 的 Element 类。
    lib2.Element element2 = lib2.Element();
    
    • showhide可以有选择地导入代码库:
    // 只导入 lib1 中的 foo。(Import only foo).
    import 'package:lib1/lib1.dart' show foo;
    // 导入 lib2 中除了 foo 外的所有。
    import 'package:lib2/lib2.dart' hide foo;
    
    • deferred as来标识需要延时加载的代码库:
    import 'package:greetings/hello.dart' deferred as hello;
    

    当实际需要使用到库中API时先调用loadLibrary函数加载库:

    Future greet() async {
      await hello.loadLibrary();
      hello.printGreeting();
    }
    

    未完待续

    相关文章

      网友评论

          本文标题:Dart Tips

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