Dart 入门 & 与 ts 类型系统的异同

栏目: JavaScript · 发布时间: 6年前

内容简介:可以使用 var 进行声明一个常量使用 var 关键词进行声明的时候,dart 会自动推断出 当前变量的类型,如果在变量声明的时候没有进行赋值,那么该类型就是动态的,类似于 TS 的 any。在类型推断上跟final 声明一个常量,只能被初始化一次。而且在初始化的时候必须赋值。其类似于 js 中的 const。final 的变量时运行时初始化

1. var 关键字

可以使用 var 进行声明一个常量

main() {
  var str = 'abc';
  str = '123';
  print(str);  // -> 123
}
复制代码

使用 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 类型
复制代码
  • num

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}
      复制代码
  • 更多

  • map 的属性是可保证顺序的

js 非常坑的一点是,在遍历一个对象的时候,并不能保证属性的顺序。但是 dart 可以。

js 中遍历一个对象的时候

var obj = {
    3: 'a',
    2: 'b',
    1: 'c',
};
var arr = [];
for(var key in obj){
  arr.push(key);
}
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 Obiect 类型

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

Obiect 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

有时候一个库很大,或者一个库里需要有公用的变量或方法,可以使用 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'
复制代码

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


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Clean Architecture

Clean Architecture

Robert C. Martin / Prentice Hall / 2017-9-20 / USD 34.99

Practical Software Architecture Solutions from the Legendary Robert C. Martin (“Uncle Bob”) By applying universal rules of software architecture, you can dramatically improve developer producti......一起来看看 《Clean Architecture》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具