Dart 入门 & 与 ts 类型系统的异同
栏目: JavaScript · 发布时间: 5年前
内容简介:可以使用 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 方法。如果显式的去声明的话,使用 set
与 get
关键字
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] # 执行某个脚本 复制代码
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 支持静态类型,但并不是强制的,如果不静态声明则默类型为
-
静态作用域 & 拥有块级作用域
dart 跟 js 一样是静态作用域。但是 Dart 有块级作用域,在 {}
外访问不到 {}
内的代码。
{ var a = 1; } print(a); // Error: Getter not found: 'a' 复制代码
上面打印的时候访问不到 {} 内的变量,所以报错了
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- C++/Golang的数组类型异同
- DevOps与敏捷异同 - DZone DevOps
- 浅谈集群、分布式、微服务的异同
- 【译】Array与Set的异同及使用场景
- 【译】Object与Map的异同及使用场景
- 服务容错模式:舱壁模式、熔断器的异同点
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。