相关文章推荐
安静的生菜  ·  “npm run dev”和“npm ...·  5 月前    · 
心软的柿子  ·  Java ...·  1 年前    · 

使用 var 关键词进行声明的时候,dart 会自动推断出 当前变量的类型,如果在变量声明的时候没有进行赋值,那么该类型就是动态的,类似于 TS 的 any。在类型推断上跟 TypeScript 是一致的。

var str = 'abc';
str = 123;
print(str);  // -> Error: int 值不能分配给 String 类型的变量 
// 声明变量时不赋值
var some;
some = 'abc';
str = 123;
print(some);  // -> 123

2. final 关键字

final 声明一个常量,只能被初始化一次。而且在初始化的时候必须赋值。其类似于 js 中的 const。final 的变量时运行时初始化

final str = 'abc';
str = 'def';
print(str);  // -> Setter not found: 'str'.
// or
final some;
print(some);  // -> must be initialized
  • 与 js 中的 const 一样,虽然不可以被重新赋值,但是允许修改其成员
  • final list = [1, 2];
    list[0] = 3;
    print(list);  // -> [3, 2]
    

    3. const 关键字

    const 与 final 类似,也是表示一个常量,但其表示的是一个编译时的常量,这个常量既可以是一个变量也可以是一个值。其特性与 final 相似,但是有很大差别,其在编译时初始化。

  • const 声明,其值不能接受一个变量
  • var some = 'abc';
    final some2 = some;
    print(some);  // -> abc  没问题
    var str = 'abc';
    const str2 = str; 
    print(str2);  // -> Error: Not a constant expression
    
  • 与 final 不同的是,const 声明的变量,其内部成员也不允许被更改值
  • const list = [1, 2];
    list[0] = 3;
    print(list);  // -> Cannot modify an unmodifiable list
    
  • 可以用 const 创建一个常量值,其表示,这个值是不能更改得。但是这跟变量的赋值没关系。
  • var list = const[1, 2];
    list[0] = 3;  // Error: Cannot modify an unmodifiable list
    list = [3, 4];
    print(list);  // -> [3, 4]  list 不是常量,仍然可以被重新赋值
    
  • 如果想定义一个常量,并且不允许其成员变化,可以配合 final 使用
  • final list = const[1, 2];
    list[0] = 3; // -> Error: Cannot modify an unmodifiable list
    list = [3, 4];  // -> Error: Setter not found: 'list'
    

    4. 显式的声明变量

    dart 支持静态类型,直接静态声明的话,即在声明时候就规定了其类型,不能赋值非指定类型的值。

    String str = 'abc';
    str = '123';
    str = 2333;  // Error: int 类型不能赋值给 String 类型的变量
    

    如果不使用静态声明,则默认的类型为 dynamic

    var bar = 123;
    print(bar is dynamic);  // true
    

    *注:关键字 is 用于类型判断,返回 bool

    5. 可以类型声明与变量声明关键字一起使用

    final int number = 123;
    number = 233;  // Error: Setter not found: 'number'.
    const bool flag = true;
    flag = false; // Error: Setter not found: 'flag'.
    var String str = 'abc';
    str = 'edg';
    

    6. 默认值

    如果在定义变量的时候没有初始化值,那么他的初始值是 null。除了常量,因为常量定义时必须有初始值

    var some;  // -> null
    bool flag;  // -> null
    int number;  // -> null
    String str;  // -> null
    Object obj;  // -> null
    final namic;  // Error: must be initialized
    

    2. 数据类型

    2.1 String

    String str = 'abc';
    
  • 模板字符串。使用方法与 js 一样
  • String name = '小明';
    print('${name} 是个直男');  // -> 小明是个直男
    
  • 多行字符串
  • 与 js 的反引号不同,dart 需要使用三个单引号或双引号表示多行字符串

    String breakStr = '''
        这是一段,
        多行字符串
    

    2.2 numbers

    数字类型有三种,int 、double 和 num

  • int 整形
  • int number = 123;
    num = 1.1;  // Error:不能将 double 类型的值赋值给 int 类型
    
  • double 浮点数
  • double number = 1.1;
    number = 2;  // Error: 不能将 int 类型的值赋值给 double 类型
    

    int 与 doubled 都是 num 的子类,其相当于 int 与 double 的联合类型。

    num number = 1;
    number = 2.33;   // 2.33
    

    2.3 bool

    与其他编程语言一样,她只有两个值。true or false

    bool flag = 1 == 1;
    print(flag);  // -> true
    
  • 与 js 不同的是,dart 在比较值的时候并不会对比较值自动进行类型转换
  • bool flag = 1 == '1';
    print(flag);  // -> false
    
  • dart 的 boolean 的显式的,只有 true 才会在判断时为 true
  • var flag = 1;
    if(flag) {
      print('真');
    } else {
      print('假');
    // Error: 不能将 int 类型的值赋值为 bool 变量
    

    上面的代码在 js 中不会有问题,但是在 dart 中会报错,因为 flag 不是布尔值

    2.4 List

    列表,类似于 js 中的 Array

  • 使用 new 关键字
  • 直接使用list字面量
  • var arr1 = new List();
    print(arr1);  // -> [];
    var arr2 = [1, 2, '3', {}];
    print(arr2);  // -> [1, 2, '3', {}];
    
  • 也可以像 ts 一样,指定 list 的成员类型
  • List<int> arr = [1, 2];
    arr = [1, 2, '3'];  // Error: String 不能分配给 int 类型的变量
    
    var arr = new List<int>();
    // 添加成员
    arr.add(1);  // -> [1]
    // 添加多个成员, 类似于 js 的 concat,但只能接受一个参数
    arr.addAll([2, 3]); // -> [1,2,3]
    // 获取索引, 相应的还有 lastIndexOf
    var index3 = arr.indexOf(3);
    print(index3);  // -> 2
    // 移除某个成员,只会移除匹配到的第一个成员
    arr.remove(1);  // -> [2,3]
    // 清空 List
    arr.clear();    // -> []
    
  • 将 list 转化为 Map
  • List<int> arr = [1,2,3];
    Map<int, int> arrMap = arr.asMap();
    print(Map);  // -> {0: 1, 1: 2, 2: 3}
    

    需要注意的是,在将 list 转化为 Map 的时候,list 必须指定成员类型

    2.5 Map

    键为唯一的键值对,键与值可以是任意类型。特性有点类似 js 的 Object

  • 创建,赋值与取值。都类似于 js 的 Object, 但是没有.语法
  • var map = new Map();
    map['a'] = 'a';
    map[1] = 1;
    map[null] = null;
    print(map);  // -> {a: a, 1: 1, null: null}
    // 访问不存在的成员会返回 null
    print(map['c']);  // -> null
    
  • 指定键值的类型
  • Map<int, int> map = {
        1: 1,
        2: 2,
    print(map);  // -> 2
    
  • length
  • Map<int, int> map = {
        1: 1,
        2: 2,
    print(map.length);  // -> 2
    
    Map<int, int> map = {
        1: 1,
        2: 2,
    // 添加成员
    map.addAll({3: 3});  // -> {1: 1, 2: 2, 3: 3}
    // 移除某个成员
    map.remove(2); // -> {1: 1, 3: 3}
    // 清空成员
    map.clear();  // -> {}
    
  • 校验是否有成员
    Map<String, int> map = {
      'a' :1,
    // 是否有该 key
    bool hasKey = map.containsKey('a');
    // 是否有该 value
    bool hasValue = map.containsValue(2);
    print(hasKey);    // -> true
    print(hasValue);  // -> false
    // 是否是一个空对象
    print(map.isEmpty);  // -> false
    
    var map = {
        1: 1,
        1: 1,
        3: 3
    // 移除符合的某一条件的属性
    var filterAttr = map.removeWhere((key, value){
        return value < 2;
    print(map); // {3: 3}
    console.log(arr);  // [1, 2, 3]
    

    上面的代码中,我遍历了一个对象,想将其 key 依次 push 进一个数组,我期望得到的是 [3, 2, 1],但是的得到的确是 [1, 2, 3], 因为 javascript 并不能保证 object 属性的顺序

    dart 中遍历一个 map

    Map<int, String> map = {
    	3: 'a',
    	2: 'b',
    	1: 'c',
    List<int> arr = [];
    map.forEach((key, value){
      arr.add(key);
    print(arr);  // [3, 2, 1]
    

    map 会按照定义时的属性的位置顺序进行遍历,正确的打印出 [1, 2, 3]

    2.6 Function(方法)

    Function 在 javascript 中是一等公民,既可以当做方法赋值给变量,也可以作为参数,也可以将实例的函数类型的属性当做方法使用。Dart 的Function 与 它类似

  • 定义一个 方法
  • int sum(int x, int y) {
        return x + y;
    sum(1, 2);  // 3
    

    上面是一个求和方法,接受两个参数,规定必须是int 类型,其 returns 类型的声明是放在最前面的,与 typeScript 不同,typeScript 是 在函数头后面声明

    箭头函数使用方法与 js 一样

    int sum(int x, int y) => x + y;
    

    与 ts 不同的是,dart 不是使用来标记可选参数,而是使用 []

    int sum(int x, int y, [int z]) {
      if(z == null) {
        return x + y;
      return x + y + z;
    sum(1, 2);  // -> 3
    sum(1, 2, 3)  // -> 6
    
  • 默认参数值
  • 默认参数与 js 使用方法一样。在定义方法的时候直接参数 = value, 但是默认值只能加给可选参数

    int sum(int x, int y, [int z = 3]) {
      return x + y;
    sum(1, 2);  // -> 6
    

    如果给必选参数加默认值会报错

    int sum(int x, int y, int z = 3) {
      return x + y;
    // Error:  Non-optional parameters can't have a default value.
    
  • 可选命名参数
  • 可以给参数指定名字,未指定名字的参数为未知参数。有些时候在使用可选参数的时候并不明确参数的定义,可以使用命名参数,在使用方法的时候必须加上可选参数的名字

    int sum(int x, int y, {int z: 3}) {
      return x + y + z;
    sum(1, 2, z: 3);  // -> 6
    sum(1, 2, 3);  // -> Error: 应该有两个位置参数,但发现了三个
    

    匿名函数就是声明时没有进行命名的函数

    List<int> arr = [1, 2, 3, 4, 5];
    arr.forEach((v) {
      print(v);  // 1, 2, 3, 4, 5
    

    上面的代码将一个打印的匿名方法传进forEach

    dart 的每一个函数都有返回值,如果没有 return 返回值则自动 return 一个 null

    add(int x) {}
    var autoNull = add(1);
    print(autoNull);   // null
    

    2.7 dynamic 类型

    如果不确定该变量的值是什么类型, 可以使用 dynamic,表示该类型的类型是动态的,类似于 TspeScript 中的 any。与 typeAcript 一样,并不推荐使用它,因为错误的类型使用不会在编辑时报错但是会在运行时报错

    dynamic some = 'abc';
    some = some + 123;  // 编译时不会报错,但是运行时会报错
    

    2.8 Object 类型

    Object 表示任意类型,object之所以能够被赋值为任意类型的原因,因为所有的类型都派生自 Object.

    Object some = 'str';
    some = 1;
    some = true;
    

    3. class

    每个对象都是一个类的实例,所有的类都继承于 Object。 基于 Mixin 的继承 意味着每个类(Object 除外) 都只有一个超类,一个类的代码可以在其他 多个类继承中重复使用。class 不可以定义在 main 主函数中

    class Person {
      String name = '汤姆';
      int age = 8;
    main() {
        var tom = new Person();
        print(tom.name);  // '汤姆'
    
  • 构造函数(constructor)
  • 跟 js 不一样,dart 的构造函数是一个与 class 同名的函数。

    class Person {
      String name;
      int age;
      // 与 class 同名的构造函数
      Person(String name, int age){
        this.name = name;
        this.age = age;
      void sayHi() {
        print('my name is ${this.name}, 今年${this.age}岁');
    main() {
      Person xiaohong = new Person('小红', 8);
      xiaohong.sayHi();  // -> my name is 小红, 今年8岁
    
  • 构造函数不能被继承,但是子类会默认的去调用父类的没有参数的构造函数
  • class Person {
      String name;
      Person(){
        this.name = '我是谁';
    class People extends Person {
      int age;
      People() {
        this.age = 18;
    People xiaohong = new Person();
    print(xiaohong.name);  // 默认调用了父类的构造函数,所以 name 属性有值
    print(xiaohong.age);
    
  • 命名构造函数
  • 在一个类里面可以创建多个构造函数,其命名方式为构造函数名.xxx创建实例的时候,可以选择不同的构造函数进行创建。

    class Person {
      String name;
      String age;
      Person(this.name, this.age);
      Person.fromMap(Map<String, String> param) {
        this.name = param['name'];
        this.age = param['age'];
      sayHi() {
        print('$name, 今年$age岁');
    main() {
      // 使用普通的构造函数创建实例
      Person xiaohong = new Person('小红', '8');
      xiaohong.sayHi();  // 小红, 今年8岁
      // 使用命名构造函数 fromMap 来创建实例
      Map<String, String> userInfo = {
        'name': '小明',
        'age': '6'
      Person xiaoming = new Person.fromMap(userInfo);
      xiaoming.sayHi();  // 小明, 今年6岁
    
  • 手动调用父类的构造函数
  • :super.父类的命名构造函数名 就可以手动调用父类的构造函数, 调用顺序为先调用父类的函数再执行自己的构造函数

    class Person {
      String name;
      Person.initName(){
        this.name = '我是谁';
        print(1);
    class People extends Person {
      int age;
      People():super.initName() {
        this.age = 0;
        print(2);
      sayHi() {
        print('$name, 今年$age岁');
    main() {
      People xiaohong = new People();
      // -> 1
      // -> 2
      xiaohong.sayHi();  我是谁, 今年0
  • 构造函数重定向
  • 构造函数的声明中可以调用其他的构造函数,此时,该构造函数没有函数体

    class Person {
      String name;
      Person.name(String name) {
        this.name = name;
      // 构造函数 init 调用了构造函数 name
      Person.init(String name): this.name(name);
      sayHi() {
        print('我叫$name');
    main() {
      Person xiaohong = new Person.init('小红');
      xiaohong.sayHi();  // 我叫小红
    
  • 工厂方法构造函数
  • 如果一个构造函数并不总是返回一个新的对象,则使用 factory 来定义 这个构造函数。下面的类是为了创建 person 并避免创建重名的 person,创建新的对象后缓存起来,如果创建重名的就从已有的对象里面去取。

    工厂方法构造函数里不能访问 this

    class Person {
      String name;
      static final Map<String, Person> personMap = <String, Person>{};
      factory Person(String name) {
        if (personMap.containsKey(name)) {
          print('catch person');
          return personMap[name];
        } else {
          print('new person');
          Person newPerson = new Person.create(name);
          personMap[name] = newPerson;
          return newPerson;
      Person.create(String name) {
        this.name = name;
      sayHi() {
        print('my name is ${name}');
    main() {
      Person xiaohong = new Person('小红');  // -> new person
      xiaohong.sayHi();  // -> my name is 小红
      Person xiaoming2 = new Person('小红');  // -> catch person
      xiaoming2.sayHi();  // -> my name is 小红
    

    上面创建的第二个小红,其实就是取得第一个小红,并没有创建新对象。

  • 在 class 中访问 this 的成员的时候,可以忽略 this.xxx, 当未声明变量的时候会自动寻找 this 中有没有该属性
  • class Person {
      String name;
      int age;
      Person(String name, int age){
        this.name = name;
        this.age = age;
      void sayHi() {
        // 这里手动定义的 age 优先级比 this.age 高
        var age = 99;
        // 没有定义 name,会自动寻找 this.name
        print('my name is ${name}, 今年${age}岁');
    main() {
      var xiaohong = new Person('小红', 8);
      xiaohong.sayHi();  // -> my name is 小红, 今年99岁
    
  • setters 与 getters
  • setters 与 getters 是用来设置与获取属性值的函数,一般情况下不需要手动设置,因为每个实例成员都有隐性的 getter 方法,非 final 的成员也会有隐性的 setter 方法。如果显式的去声明的话,使用 setget 关键字

    set 与 get 函数中都不可以访问自己,否则会死循环
    set 函数中可以修改其他成员的值

    下面的例子显式的声明了 Point 的 y 的 set 与 get 函数

    class Point {
      int x;
      Point(this.x);
      int get y => x + 3;
      set y(int value) => x = value;
    main() {
      Point point = new Point(5);
      point.y = 10;
      print(point.x);
      print(point.y);
    
  • 抽象类与抽象函数
  • 抽象类不能实例化,需要继承他的子类去实例。抽象函数是只有函数名但是没有函数图,需要子类去实现具体的功能

    抽象函数用分号代替函数体
    抽象函数必须在子类中被实现

    abstract class Person {
      sayHi();
    class Student extends Person {
      sayHi() {
        print("I'm a student");
    main() {
      Person student = new Student();
      student.sayHi();
    

    dart 没有提供 interface 关键字用来声明接口,
    但是可以使用implements关键字将 class 当做接口使用

    /// 一个抽象类 Person
    abstract class Person {
      String name;
    /// 使用 implements 关键字把 Person 当做接口使用
    /// Teacher 必须含有 Person 的格式
    class Teacher implements Person {
      String name;
    main() {
      var xiaohong = new Teacher();
      print(xiaohong);  // -> Instance of 'Teacher'
    

    在使用接口的时候,并不是像 typescript 一样必须严格一致,dart 里只需要实现接口内的结构就不会报错,可以有自己的成员。

    /// 作为 Person 接口的实现,Teacher 多了个 age 成员,但是没有问题
    class Teacher implements Person {
      String name;
      int age;
    

    4. 异常

  • catch
  • 可以使用 try catch 语句来捕获异常

    try {
        dynamic foo = true;
        print(foo++);   // 运行时错误
    } catch (e) {
        print('错误类型: ${e.runtimeType}');  // -> 错误类型: NoSuchMethodError
    } finally {
        print('无论有没有异常都会执行');  
    
  • 主动抛出错误
  • 可以使用 throw 主动抛出错误,也可以在 catch 里使用 rethrow 关键字继续将捕获到的错误抛出

    throw 抛出的异常可以是任意类型

      try {
        throw '一个字符串错误';  // 主动抛出一个 String 类型的错误
      } catch (e) {
        print('错误类型: ${e.runtimeType}.');  // 错误类型: String.
        rethrow;   // 使用 rethrow 关键字继续将错误抛出
    
  • 根据错误类型分层处理
  • 可以使用 on 声明要在块里捕获的错误类型,顺序从上往下类似于 switch;

      try {
        throw '一个字符串错误';
      } on String catch(e) {
        print('捕获 String 类型异常: ${e.runtimeType}.');  // 捕获 String 类型异常: String
      }catch (e) {
        print('捕获所有类型异常: ${e.runtimeType}.');
    

    5. 类型判断

    dart 可以使用 is 关键字进行类型判断。比 typescript 与 javascript 的 typeof 更加方便、准确

    var str = 'string';
    print(str is String);  // true
    

    6. 泛型

    泛型又称为参数化类型,使用方法与 typescript 类型,在具体的类型后面加<类型>。dart 还可以用字面量的形式在之前面使用泛型进行注解

    List<bool> arr = [1];
    // Error: 参数类型'int'不能分配给参数类型'bool'
    var array = <int>[1, 2, 3];
    arr.array('4');
    // Error: 参数类型'String'不能分配给参数类型'int'
    

    上面两种泛型的使用方式都能对类型进行保护

  • dart 的泛型是固化的,在运行时的代码也可以进行正确的类型检查;
  • var arr = new List<int>();
    arr.addAll([1, 2, 3]);
    print(names is List<int>);  // -> true
    

    泛型函数可以在以下地方使用泛型

    函数内变量

    T getFirstItem<T>(List<T> arr) {
        T firstItem = arr[0];
        return firstItem;
    var first = getFirstItem<int>([1, 2, '3']);
    print(arr);  // Error: 不能将参数类型 String 分配给 参数类型 int
    

    上面代码因为 getFirstItem 在使用的时候声明泛型是 int,但是传入的 List 内却有 '4',所以报错了

  • extends
  • 可以使用 extends 关键字来缩小泛型的类型范围,使用方式与 ts 一样

    7. typedef

    可以给方法的类型起个名字,用来定义方法的样子。类似于 ts 的 type,不过只能用于方法

    typedef int Add(int x, int y);
    main() {
      Add add = (x, y) => x + y;
      var sub1 = add(1, '2');  // Error: 不能将 String 类型的参数赋值给 int 类型的参数
      var sub2 =  add(1, 2);  // -> 3
      print(sub2 is int);     // -> true
    

    上面定义了一个名为Add的方法类型别名,其作为方法 add 的类型,因为 sub1 传入的参数类型与 Add 定义的不符,所以报错了。 因为 Add 定义了 add 方法的返回值是 int 类型,所以 (sub2 is int) 为 true

    8. 模块化

    7.1 pubspec.yaml

    创建pubspec.yaml文件来声明该项目需要的依赖包,在 使用 put 命令拉取依赖包的时候会生成一个 .packages 文件,该文件将项目所依赖的每个程序包名称映射到系统缓存中的相应包

    name: myApp
    dependencies:
      js: ^ 0.3.0
      intl: ^ 0.12.4
    

    7.2 pub

    dart 使用 pub 工具来管理 package 和 assets。在下载 Dart 的时候会带有 pub不需额外下载

    pub get  # 获取依赖项
    pub upgrade  # 更新依赖项
    pub run [some script] [args]  # 执行某个脚本
    

    pub 命令

    7.3 import

    引入内置的库: dart: 库名

    import 'dart:io';
    

    可以使用 import 语句从 package 引入某个模块,要加 package 声明

    // 使用 import as 语句引入某个模块
    import 'package:js/js.dart' as js;
    // 引入 js 模块的一部分 - anonymous
    import 'package:js/js.dart' show anonymous;
    

    引入库除此之外的其他部分

    // 引入 js 库除 anonymous 的部分
    import 'package:js/js.dart' hide anonymous;
    

    直接使用相对路径引入自己的模块

    import '../someLib.dart';
    

    7.4 按需加载

    dart 直接提供了按需加载的语法,可以让应用在需要的时候再加载库。比如说一个页面中可能有不同的状态,不同的状态下可能需要不同的依赖库,这时候使用按需加载的话就能减少不必要的资源与性能浪费。这非常非常的方便!想想一下在写 js 业务的时候,在一个有多种状态的页面需要把每一个状态的依赖加载下来。

    想要延迟加载一个库,使用 deferred 关键字来导入

    import 'package:js/js.dart' deferred as JS;
    

    使用的时候,异步的方式调用库的 loadLibrary 函数就可以, 异步语法与 js 相似

    int flag = true;
    getJs() async {
        await Js.loadLibrary();  // 引进 JS ku
        // 下面的代码可以使用 JS 库了
    

    当延迟加载库时,整个库内的内容都是为加载过来的,包括库内的类型声明。所以如果想在使用库前使用库内的类型声明,可以把这个库的类型声明抽出来单独作为一个文件。

    7.5 创建一个自己的包

    包文件:person.dart

    library person;  // 指定库名,用来生成文档。
    class Teacher {
      String name;
      Teacher(this.name) {
        print('我是 ${name} 老师');
    class Student {
      String name;
      Student(this.name) {
        print('我是学生 ${name}');
    

    上面创建了一个 person.dart 文件,里面定义了两个类 teacher 和 student,不需要导出,使用的时候直接引进来就行。 library 一般不需要手动声明,因为 dart 会自动生成唯一的库标识,除非想要生成库 文档。

    import './common/person.dart' as person;
    main() {
      var xiaohong = new person.Teacher('小红');  // -> 我是 小红 老师
      var xiaoming = new person.Student('小明');  // -> 我是学生 小明
    

    也可以使用 show 关键字只导入某一个部分

    import './common/person.dart' show Teacher;
    main() {
      var xiaohong = new Teacher('小红');  // -> 我是 小红 老师
    

    有时候一个库很大,或者一个库里需要有公用的变量或方法,可以使用 part 语法进行拆分。

    还是上面 person 的例子,假如 Teacher 类与 Student 类都有一个 sayHi 的函数,而这两个类的 sayHi 函数的逻辑是一样的,这里就可以将这个逻辑单独抽出来放到一个独立的文件中去实现。

    person 库:

    library person;
    part 'commonSayHi.dart';  // 声明sayHi的实现 - commonSayHi 的路径
    class Teacher {
      String name;
      Teacher(this.name);
      sayHi() {
        commonSayHi(this.name);
    class Student {
      String name;
      Student(this.name);
      sayHi() {
        commonSayHi(this.name);
    

    commonSayHi:

    part of 'person.dart';  // 声明这是用于 person.dart 文件的实现
    commonSayHi (name) {
      print("Hello, I'm ${name}");
    
    import './common/person.dart' show Teacher;
    main() {
      var xiaohong = new Teacher('小红');
      xiaohong.sayHi();  // Hello, I'm 小红
    

    ?. 总结 - Dart 的语言特性

  • 强类型,静态类型检查
  • dart 支持静态类型,但并不是强制的,如果不静态声明则默类型为 dynamic
  • 静态作用域 & 拥有块级作用域
  • dart 跟 js 一样是静态作用域。但是 Dart 有块级作用域,在 {}外访问不到{}内的代码。

    var a = 1; print(a); // Error: Getter not found: 'a'

    上面打印的时候访问不到 {} 内的变量,所以报错了

    分类:
    前端
    标签:
  •