一.简介
Dart is a client-optimized language for fast apps on any platform.
Dart
是谷歌开发的计算机编程语言,可用于移动应用、网页、服务器等领域。它是一门面向对象的语言,也是强类型的、类定义的、单继承的语言,支持接口、混入(Mixins)、抽象类、泛型、可选类型等。它融合了许多现代编程语言的优秀特性,从中能看到JavaScript、Swift等的影子。
二.语法、特性 本篇主要用于记录接触Dart
以来我个人比较感兴趣的一些特性和语法,部分内容会与Swift做比较,以便更好的理解其语法特性。Dart提供了一个基于浏览器的DartPad 工具以便调试代码,本文所有代码均可在线调试。开讲~
1.数据类型 Dart 语言支持以下内建类型:
num
String
Boolean
List
Map
Set
Rune(表示字符串中的 UTF-32 编码字符)
Symbol(符号,如根号√ ̄)
num
表示数值型,包括整型int
和浮点型double
,没有float
。其中 int 可转为 double 类型,反之则不行。
1 2 3 4 5 6 7 8 9 10 void main() { var a = 1.0 a = 1 num b = 1 b = 1.0 var c = 1 c = 1.0 }
num
类型与String
类型可相互转换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void main() { var a = 1.0 ; a = double .parse('2.0' ); print (a); var b = a.toString(); print ('+++$b , type:${b.runtimeType} ' ); int c; c = int .parse("3" ); var d = c.toString(); print ('+++c:$c ,d:$d , type:${d.runtimeType} ' ); num e = 4 ; final f = e.toString(); e = double .parse('5.0' ); print ('+++f:$f ,e:$e ' ); }
Dart 中的集合与 Swift 相似,具备自动推断类型
的能力:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // 数组 var list = [1 , 2 , 3 ]; assert(list.length == 3 ); assert(list[1 ] == 2 );// 这里 Dart 会推断 list 的类型为 List<int> 。 // 如果将非整数对象添加到此 List 中, 则会报错。 list[1 ] = 1 ; assert(list[1 ] == 1 );// 字典// key可以是任何类型-与Swift类似 var gifts = { // Key: Value 'first' : 'partridge' , 'second' : 'turtledoves' , 'fifth' : 'golden rings' }; //gi fts 的类型会被推断为 Map<String, String> var nobleGases = { 2 : 'helium' , 10 : 'neon' , 18 : 'argon' , }; // nobleGases 的类型推断为 Map<int, String>
2.万物皆对象 Dart中万物皆对象,所有对象都直接或间接继承自Object
类,无论是数字,函数还是 null 都是对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 enum Color { red, green, blue }void main() { var a = 1 ; print (a is Object ); a = null ; print ('${a.runtimeType} , ${a is Object} ' ); var x = Color.blue; print (x is Object ); int aInt; print (aInt); }
3.动态类型 一般声明变量时都会明确申明变量的类型,或者通过类型推断
来自动识别变量的类型:
1 2 int a = 1 ; // 指定为int var b = 2 ; // 推断为int
但如果对象不限定为单个类型,或者暂时不知道是什么类型,则可以指定为对象类型
或动态类型
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 enum Color { red, green, blue }void main() { List <Object > arr; arr = [1 ,"x" ,Color.red]; var x = arr[2 ]; if (x is int ){ print ('$x is int' ); }else if (x is String ) { print ('$x is String' ); }else if (x is Color) { print ('$x is enum' ); } dynamic name = 'Bob' ; name = 1 ; print ('name is:$name ' ); }
4.常量 1.赋值时机 Dart中声明常量时有两种标识符const
与final
。
const修饰编译时常量;
final可修饰编译时常量或运行时常量;
编译时常量
是指常量的值在编译时就已经确定,而运行时常量
的值在运行时才确定:
1 2 3 4 5 6 7 8 9 10 class A { } void main() { num a = 1 final b = a.toString() const c = a.toString() final c = A() const d = A() }
2.集合可变性 集合的可变性是由【其本身的修饰符】和【其字面量的修饰符】共同决定的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // 基本类型 final name = 'Bob' ; // Without a type annotation final String nickname = 'Bobby' ; name = 'Alice' ; // Error: 一个 final 变量只能被设置一次。// 集合 var constantList = const [1 , 2 , 3 ];// constantList[1 ] = 1 ; // 取消注释会引起编译时异常。 const constantList2 = [1 , 2 , 3 ]; constantList[1 ] = 1 ; // 取消注释会引起编译时异常。 final constantList2 = [1 , 2 , 3 ]; constantList2[1 ] = 1 ; // 取消注释不会引起错误。 var constantList3 = final [1 , 2 , 3 ]; // 报错
可以这么总结:
final不能修饰字面量;
const可定义编译时常量,也可以用来定义不可变字面量;
final和const常量都只能赋值一次;
final集合变量不能重新赋值,但集合内的元素可修改;
const集合或字面量不能重新赋值,集合内元素也不可修改。
3.用const优化性能 const
修饰的两个常量的值相同,则二者指向同一片内存空间:
1 2 3 4 5 6 7 8 9 10 11 12 void main() { var a = Object (); var b = Object (); print (identical(a,b)); const a1 = Object (); const b1 = Object (); print (identical(a1,b1)); }
用const
创建多个相同的对象时,内存中只会保留一个对象。
因此,在优化性能时,可以考虑合理使用const
关键字这个方法。
5.函数&可选参数
如果函数体中只有一行语句
或一个表达式
时,可以使用=> expr
简写语法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 void printInt(int i) { print (i); }void printInt1(int i) => print (i); add0(int a, int b){ a+b; }int add1(int a, int b) { return a+b; }int add2(int a, int b) => a+b; add3(int a, int b) => a+b; add4() => ((){ print ("++++this is A block+++" ); }); add5() => (() => print ("++++this is B block+++" ));void main() { print (add0(0 ,1 )); print (add1(0 ,1 )); print (add2(0 ,1 )); print (add3(0 ,1 )); add4()(); add5()(); }
注意:add0
不能改成胖箭头写法,否则=>
会把其后的数值变成返回值,从而改变函数类型。
也就是说:=> expr
!= {return expr;}
在箭头=>
和分号;
之间只能使用一个表达式 ,不能是多语句。
定义函数时,使用花括号 {param1, param2, …} 来指定命名参数;
将参数放到 [] 中来标记参数是可选的;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Bird { void tweet(int a, int b){ print ("tweet: JIO JIO JIO~" ); } int tweet2({int a, int b, int c}){ if (b is Null ){ b = 0 ; } print ("tweet2: $a + $b + $c JIO JIO JIO~" ); return a+b; } int tweet3(int a, [int b, int c]){ print ('tweet3: b:$b ,c:$c ' ); } }void main() { var b = Bird(); b.tweet(1 , 2 ); b.tweet2(a:1 , b:2 ); b.tweet2(a:1 ); b.tweet3(1 ); b.tweet3(1 , 2 ); }
一个参数只能选择【命名可选参数】和【位置可选参数】其中一种方式修饰。
和Swift一样,定义方法时使用 = 来定义可选参数的默认值,默认值只能是编译时常量。
1 2 3 4 5 int add (int a, [int b = 0 ] ) => a+b;void main () { print(add (1 , 2 )); print(add (1 )); }
6.函数是一等公民 与Swift一样,Dart中函数也是一等公民,这意味着一个函数可以作为另一个函数的参数。示例1:
1 2 3 4 5 6 7 8 void printElement (int element ) { print(element); }void main () { var list = [1 , 2 , 3 ]; list.forEach(printElement); }
同样可以将一个函数赋值给一个变量,例如:
1 2 3 4 5 6 7 8 9 String printElement (String element ) { var x = '$element' .toUpperCase (); print (x); return x; }void main ( ) { var loudify = printElement; print (loudify ('hello' ) == 'HELLO' ); }
但是,Dart中函数作为参数时,会有个小困扰 ,示例2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int mathAdd(int a , int b ) { return a + b; }int mathMultiple(int a , int b ) { return a * b; }int mathIncrease(int a ) { return ++a; }int math1(int a, int b, Function mathOperation) { return mathOperation(a , b ) ; }int math2(int a, Function mathOperation) { return mathOperation(a ) ; } main() { print(math1(1 , 2 , mathAdd)); print(math1(1 , 2 , mathMultiple)); print(math2(1 , mathIncrease)); }
示例2中,当方法作为参数(mathOperation)时,Dart只是将mathOperation标注为Function
类型:
1 2 3 int math1 (int a, int b, Function mathOperation) { return mathOperation (a, b) ; }
但从Function
关键字上并看不出mathOperation代表的函数有几个参数,参数分别是什么类型,返回值是啥,这有时很容易让人困惑。相较而言,Swift中将函数作为参数时,会明确标明此函数的类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func addTwoInts (_ a : Int , _ b : Int ) -> Int { return a + b }func multiplyTwoInts (_ a : Int , _ b : Int ) -> Int { return a * b }func printMathResult (_ mathFunction : (Int , Int ) -> Int , _ a : Int , _ b : Int ) { print ("Result: \(mathFunction(a, b)) " ) } printMathResult(addTwoInts, 3 , 5 )
这里(Int, Int) -> Int
即明确指明了mathFunction
参数的函数签名,清楚明了。
为了解决这个问题,Dart也提供了自己的解决方案:使用Typedefs
为函数起一个别名, 别名可以用来声明字段及返回值类型。 当函数类型分配给变量时,typedef会保留类型信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 typedef aliasType = int Function(int a , int b ) ; int mathAdd(int a , int b ) { return a + b; }int mathMultiple(int a , int b ) { return a * b; }int math1(int a, int b, aliasType mathOperation) { return mathOperation(a , b ) ; } main() { print(math1(1 , 2 , mathAdd)); print(math1(1 , 2 , mathMultiple)); }
7.闭包 大部分方法都带有名字,你也可以创建没名字的方法,称之为匿名函数
或者闭包
。
匿名函数和命名函数看起来类似— 在括号之间可以定义一些参数或可选参数,参数使用逗号分割。
后面大括号中的代码为函数体:
1 2 3 ([[Type] param1[, …]] ) { codeBlock; };
示例1:
1 2 3 4 void main() { var loudify = (msg) => '${msg.toUpperCase()} ' ; print (loudify('hello' ) == 'HELLO' ); }
示例2:
1 2 3 4 var list = ['apples' , 'bananas' , 'oranges' ]; list.forEach((item) { print ('${list.indexOf(item)} : $item ' ); });
8.链式/级联 级联运算符 ..
可以像RAC一样,实现对同一个对像的一系列链式操作。 除了调用函数,还可以访问同一对象上的字段属性。
1 2 3 4 5 var button = querySelector('#confirm' );button .text = 'Confirm' ;button .classes.add('important' );button .onClick.listen((e) => window .alert('Confirmed!' ));
使用级联操作符:
1 2 3 4 querySelector('#confirm' ) ..text = 'Confirm' ..classes.add('important' ) ..onClick.listen((e ) => window.alert('Confirmed!' ));
9.switch的值 switch
可比较整数、字符串、枚举。比较的对象必须都是同一个类的实例且类没有重写==
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 enum Color{ red, green, blue }void main() { var a = Color.red; switch (a){ case Color.red: print ("red" ); break ; case Color.green: print ("green" ); break ; case Color.blue: print ('blue' ); break ; default : print ("unknown" ); } var x = 0.1 ; switch (x){ case 0.0 : print ('0.0' ); break ; case 0.1 : print ('0.1' ); break ; default : print ('default' ); } }
10.可选链 与Swift一样,调用方法时使用?.
来代替.
, 可以避免因为左边对象可能为 null 导致的异常:
1 2 3 var p = Point (2 , 2 ); p?.y = 4 ;
11.构造函数
创建一个与类同名的函数来声明构造函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Point { num x, y, z; Point(num x, num y) { this .x = x; this .y = y; print ('x:$x ,y:$y ,z:$z ' ); } }void main() { var p = Point(0 ,1 ); }
利用语法糖对上面的构造函数进行简化:
1 2 3 4 5 6 7 class Point { num x, y, z; Point(this .x, this .y); }void main () { var p = Point(0 ,1 ); }
在【没有声明构造函数的情况下】, Dart 会提供一个默认的构造函数。
1 2 3 4 5 6 class Point { num x, y, z; }void main () { var p = Point(); }
【子类不会自动继承父类的构造函数】。如果子类不声明构造函数,那么它就只有默认构造函数:
1 2 3 4 5 6 7 8 9 10 11 12 class Point { num x, y; Point (this .x, this .y); }class SubPoint extends Point { num z; } void main() { var p = SubPoint (1 ,2 ); var p2 = SubPoint (); }
示例中会出现两处报错:
报错1是因为子类不会自动继承父类的构造函数,所以也就无法调用父类的构造函数;
报错2是因为子类的默认构造函数会自动调用父类的无参构造函数 ,而此时父类中因为提供了自定义构造函数,所以就没有无参构造函数,因此调用失败,所以子类想要调用默认构造参数。父类就不能自定义构造函数:
1 2 3 4 5 6 7 8 9 class Point { num x, y; }class SubPoint extends Point { num z; } void main() { var p = SubPoint (); }
给构造函数命名,以便更清晰的表明函数的意图,格式为【类名.函数名(参数..)】
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Point { num x, y; Point.initWithXY(this .x,this .y){ print ('called super init,x:$x ,y:$y ' ); } }class SubPoint extends Point { num z; SubPoint.initWithXYZ(num x, y, this .z): super .initWithXY(x,y){ print ('called sub init,x:$x ,y:$y ,z:$z ' ); } }void main() { var p = SubPoint.initWithXYZ(1 ,2 ,3 ); }
注意这里调用父类构造函数的语法:【在当前构造函数冒号 (:) 之后,函数体之前,声明调用父类构造函数】。
使用factory
关键字定义,可以选择性地返回类的现有实例,而不必每次都创建新对象。
用于单例模式或缓存对象,或者在构造函数返回的类型与定义的类本身不完全一致的情况下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Database { static final Database _instance = Database ._internal (); Database ._internal ( ) { _connection (); } factory Database () { return _instance; } void _connection ( ) { print ('Connected!' ); } }void main ( ) { var db1 = Database (); var db2 = Database (); print (identical (db1, db2)); }
常量构造函数以const
修饰,用于成员变量都是final
的的类。
用常量构造函数实例化多个对象时,若参数相同,则内存中只会保留一个对象,节省内存开销。
反例1:
1 2 3 4 5 6 7 8 9 10 11 12 class Box { double width; double height; Box({required this .width,required this .height}); }void main() { var a = Box(width:1 ,height:1 ); var b = Box(width:1 ,height:1 ); print (identical(a,b)); }
正例2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Box { final double width; final double height; const Box({required this .width,required this .height}); }void main() { Box a1 = const Box(width:1 ,height:1 ); Box b1 = const Box(width:1 ,height:1 ); print (identical(a1,b1)); const a2 = Box(width:1 ,height:1 ); const b2 = Box(width:1 ,height:1 ); print (identical(a2,b2)); var a3 = Box(width:1 ,height:1 ); var b3 = Box(width:1 ,height:1 ); print (identical(a3,b3)); }
注意,正例2中最后一条可以看出,调用常量构造函数创建对象时,如果不用const修饰,则这些对象不是常量实例,内存中会存在多份。
常量构造函数在优化Flutter性能 时非常有用,因为每次构建组件树时,const修饰的组件只存在一份,并且不参与重新构建。
12.初始化列表 与Swift不一样的是,Dart的构造函数中并不强制所有成员变量都完成初始化,那它们什么时候去初始化呢?Dart的解决方案是初始化列表
,即在构造函数的方法体花括号{...}
之前初始化实例变量【~~比较奇怪的语法~~】。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Point { num x, y; Point.initWithXY(this .x,this .y){ print ('called super init,x:$x ,y:$y ' ); } Point.initFromMap(Map <String ,int >dic) : x = dic['x' ], y = dic['y' ]{ print ('called super.initFromMap,x:$x ,y:$y ' ); } }class SubPoint extends Point { num z; SubPoint.initList(Map <String ,int >dic) : z = dic['z' ], super .initFromMap(dic){ if (x == 1 ){ x = 0 ; } print ('called sub.initList,x:$x ,y:$y ' ); } SubPoint.initWithXYZ(x,y,this .z): super .initWithXY(x,y){ print ('called sub init,x:$x ,y:$y ,z:$z ' ); } }void main() { var dic = {'x' :1 ,'y' :2 ,'z' :3 }; var p = SubPoint.initList(dic); }
各参数的初始化用逗号分隔,且调用父类构造函数的语句要放在初始化列表的最后。
13.Getter/Setter 和Swift一样,Dart中属性的Getter/Setter也是为计算属性
而存在的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Rectangle { num left, top, width, height; Rectangle (this .left, this .top, this .width, this .height); 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 ); print (rect.left); rect.right = 12 ; print (rect.right); print (rect.left); }
通过修改left,从而影响 left + width = right,最终完成对right属性的修改。
14.抽象类&extends 使用abstract
来定义抽象类。抽象类不能被实例化,通常用来定义接口,以及部分实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 abstract class AbstractClass { var name; var hasValue = 1 ; abstractMethod(); funcWithImplements(){ print("~~~~~~~" ); } }class ConcreteClass extends AbstractClass { var name = 1 ; var hasValue = 2 ; @override abstractMethod(){ print('++call abstractMethod()'); } @override funcWithImplements(){ print("~~~call funcWithImplements()" ); } } void main() { var a = ConcreteClass (); a.abstractMethod(); a.funcWithImplements(); }
Dart也和Swift一样,只允许单继承~
15.接口&implements Dart中移除了interface
关键字,可以通过抽象类或implements
普通类来实现接口的功能。
每个类都隐式的定义了一个接口,接口包含了该类所有的实例成员及其实现的接口。 如果要创建一个 A 类,A 要支持 B 类的 API ,但是不需要继承 B 的实现, 那么可以通过 A 实现 B 的接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 abstract class AbstractClass { var name; var hasValue = 1 ; abstractMethod (); funcWithImplements ( ){ print ("~~~~~~~" ); } }abstract class AbstractClass2 { abstractMethod2 (); }class ConcreteClass implements AbstractClass , AbstractClass2 { var name = 1 ; var hasValue = 2 ; @override abstractMethod ( ){ print ('++call abstractMethod()' ); } @override abstractMethod2 ( ){ print ('++call abstractMethod2()' ); } @override funcWithImplements ( ){ print ("~~~call funcWithImplements()" ); } }void main ( ) { var a = ConcreteClass (); a.abstractMethod (); a.abstractMethod2 (); a.funcWithImplements (); }
16.Mixin Mixin
就是“混入”的意思,用于给当前类添加【新的】【可选】功能。
通常来说,我们可以通过继承
或者实现接口
来给现有类扩展新的功能。但这些都是带有侵入性的,你可能会获得这些父类或接口中一些多余的功能,并且你需要自己提供这些功能的实现;使用 Mixin 则你只需要定义可复用的 mixin 类,在其中定义API并提供具体实现,然后就可以将这些mixin类的API通过with
关键字组合到当前类中。
继承强调的是is-a
,而 Mixin 强调的是I can
。
Mixin
是复用类代码的一种途径,复用的类可以在不同层级,可以不存在继承关系。
通过创建一个继承自Object
且没有构造函数的类,来实现一个Mixin
。 如果Mixin不希望作为常规类被使用,使用关键字mixin
替换class
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 class Devices { void run() { print('++++开机'); } void stop() { print('++++关机'); } } class iPhone extends Devices { void run() { super .run(); print('++++iphone 开机'); } } mixin iOS { void call() { print('+++iOS running'); } }class AppStore { void download() { print('++++downloading'); } }class iPhoneX extends iPhone with iOS , AppStore { void run() { super .run(); print('iphoneX 开机'); } } void main() { var p = iPhoneX(); p.run(); p.call(); p.download(); }
Dart的这个功能还是挺强大的,根据我的掌握的知识,Swift 中尚没有与之对应的关键字。不过真要说的话,或许可以借道protocol
+extension
,在不增加某个类本身的代码量的前提下,给此类扩展额外的能力:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 protocol IOS { func call () }protocol AppStore { func download () }extension IOS { func call (){ print ("+++iOS running" ) } }extension AppStore { func download (){ print ("++++downloading" ) } }class iPhone : IOS , AppStore { func run (){ print ("++++iphone 开机" ); } }let iphone = iPhone() iphone.run() iphone.call() iphone.download()
17.泛型 这里的泛型与 Swift 中的类似,都是使用<T>
格式来表示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Store <T> { var map = {}; T getByKey (String key ){ T value = map [key ]; return value; } void setByKey (String key , T value){ map [key ] = value; } }void main () { var aStore = Store (); aStore.setByKey ('first' , 1 ); print (aStore.getByKey ('first' )); aStore.setByKey ('second' , 'ii' ); print (aStore.getByKey ('second' )); }
18.is&as is
用于进行类型检查,即类是否在某个类的继承树上。或者检查对象是否实现了某个接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Person { String name; }abstract class Speak { speak(); }class Student extends Person implements Speak{ int identifier; speak(){ print ('Hello~' ); } }void main() { var p = Person(); var s = Student(); Person x = Student(); print (p is Person); print (s is Student); print (s is Person); print (x is Student); print (x is Person); print (p is Speak); print (s is Speak); }
as
用于类型转换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Person { String name; }abstract class Speak { speak(); }class Student extends Person implements Speak{ int identifier; speak(){ print ('Hello~' ); } }void main() { var p = Person(); var s = Student(); Person x = Student(); var y = x as Student; y.identifier = 101 ; print (p as Person); print (p as Student); print (s as Person); print (s as Student); print (x as Person); print (x as Student); }
19.库
库不仅提供了 API ,而且对代码起到了封装的作用。
import 和 library 指令可以用来创建一个模块化的,可共享的代码库。
Dart中没有public
、protected
和private
这些访问修饰符,约定以下划线_
定义私有类型,表示仅在当前库中可见。
每个 Dart 应用程序都是一个库 ,虽然没有使用 library 指令。
1 2 3 4 5 6 7 8 import 'dart:math' ;import 'package:test/test.dart' ;import 'path/to/my_other_file.dart' ;
如果只使用库的一部分功能,则可以选择需要导入的内容:
1 2 3 4 5 // Import only foo.import 'package:lib1/lib1.dart' show foo; // Import all names EXCEPT foo.import 'package:lib2/lib2.dart' hide foo;
如果导入两个存在冲突标识符的库, 则可以为这两个库,或者其中一个指定前缀。 例如,如果 library1 和 library2 都有一个 Element 类, 那么可以通过下面的方式处理:
1 2 3 4 5 6 7 8 import 'package:lib1/lib1.dart' ;import 'package:lib2/lib2.dart' as lib2;Element element1 = Element (); lib2.Element element2 = lib2.Element ();
20.异步 Dart 默认是单线程的,耗时操作很容易造成线程阻塞:
1 2 3 4 5 6 7 loadData(){ String dataURL = "https://jsonplaceholder.typicode.com/posts" http.Response response = http.get(dataURL) setState(() { widgets = json.decode(response.body) }) }
网络请求可能会很慢,这时主线程会被挂起,直到请求完成并更新状态。
好在 Dart 提供了异步工具async
、await
、Future
,来实现异步操作。函数体被async
标记的函数,即是一个异步函数。将async
关键字添加到函数使其返回Future
,以便检查结果。
例如,下面的同步函数,它返回一个 String :
1 String lookUpVersion() => '1 .0 .0 ';
假如将来的实现将非常耗时,将其更改为异步函数,返回值是Future
:
1 Future<String> lookUpVersion () async => '1.0.0' ;
await
与async
借鉴自ES7,一般成对出现;
出现await操作的【方法】必须声明为async;
await修饰耗时操作,此耗时操作会阻塞当前线程;
async修饰的方法会立刻返回,不阻塞当前线程;
示例1:调用时不带await
1 2 3 4 5 6 7 8 9 10 11 12 Future refreshDate() async { print ("++++3333: +${DateTime.now()} " ); await Future.delayed(Duration (seconds:5 )); print ("++++44444: ${DateTime.now()} " ); return null ; }void main() async { print ("++++111: ${DateTime.now()} " ); refreshDate(); print ("++++222: ${DateTime.now()} " ); }
日志:
1 2 3 4 ++++111 : 2020-05 -08 23:53:59.366 ++++3333 : +2020 -05 -08 23:53:59.366 ++++222 : 2020-05 -08 23:53:59.366 ++++44444 : 2020-05 -08 23:54:04.367
示例2:调用时带await
1 2 3 4 5 6 7 8 9 10 11 12 Future refreshDate() async { print ("++++3333: +${DateTime.now()} " ); await Future.delayed(Duration (seconds:5 )); print ("++++44444: ${DateTime.now()} " ); return null ; }void main() async { print ("++++111: ${DateTime.now()} " ); await refreshDate(); print ("++++222: ${DateTime.now()} " ); }
日志:
1 2 3 4 ++++111 : 2020-05 -08 23:51:24.701 ++++3333 : +2020 -05 -08 23:51:24.701 ++++44444 : 2020-05 -08 23:51:29.702 ++++222 : 2020-05 -08 23:51:29.702
Dart 还提供了Isolate
来让代码运行在其他线程中,从而实现并发编程,后续有时间继续研究~
三.后记 Dart
包含了诸多现代编程语言的特性,特点鲜明:
高效:语法清晰简洁、工具简单而强大、输入检测可尽早识别细微错误、库文件丰富;
快速:提供AOT
提前编译优化,在移动设备和web上实现高性能和快速启动;支持热加载
;
可移植:可编译成ARM
和x86
代码,支持移动、桌面及终端程序;web上会转换为JavaScript
;
易学:面向对象,语法风格与Swift
相似,上手相对容易些。
响应式:支持响应式编程。可通过 Future 和 Stream 的特性和API实现异步编程。
学习Flutter
之前有必要研究一下Dart
,对比学习从而加深对各语言的理解~
相关参考:
#©Dart中文网
#©DartPad-在线编译调试工具
#©Flutter中文网