Dart语言快速上手
Dart作为Flutter的开发语言,与其他语言有很多相似之处,我们来看一下快速上手Dart语言的相关知识。
表达式
创建并初始化变量:
1 | var name = 'Bob'; |
显式的声明类型:
1 | String name = 'Bob'; |
如果一个对象不受限于单一类型,可以指定为
Object类型(或在必要时使用dynamic)。
空安全
空安全能够防止意外访问 null 的变量而导致的错误。
空安全引入了三个关键更改:
- 当你为变量、参数或另一个相关组件指定类型时,可以控制该类型是否允许
null。要让一个变量可以为空,你可以在类型声明的末尾添加?。
1 | String? name // Nullable type. Can be `null` or string. |
- 你必须在使用变量之前对其进行初始化。可空变量是默认初始化为
null的。 Dart 不会为非可空类型设置初始值,它强制要求你设置初始值。 Dart 不允许你观察未初始化的变量。这可以防止你在接收者类型可以为null但null不支持的相关方法或属性的情况下使用它。 - 你不能在可空类型的表达式上访问属性或调用方法。同样的例外情况适用于
null支持的属性或方法,例如hashCode或toString()。
空安全将潜在的 运行时错误 转变为 编辑时 分析错误。当非空变量已经是以下情况之一时,空安全标记为非空变量:
- 使用非空值不初始化。
- 赋值为
null。
默认值
具有可空类型的未初始化变量的初始值为 null 。即使是具有数值类型的变量,初始值也为空,因为数字(就像 Dart 中的其他所有东西一样)都是对象。
1 | int? lineCount; |
对于空安全,你必须在使用非空变量之前初始化它们的值.你不必在声明变量时初始化变量,但在使用之前需要为其赋值。
1 | int lineCount; |
顶级变量和类变量是延迟初始化的,它们会在第一次被使用时再初始化。
延迟初始化
late 修饰符有两种用法:
- 声明一个非空变量,但不在声明时初始化。
- 延迟初始化一个变量。
如果你确定变量在使用之前已设置,但 Dart 推断错误的话,可以将变量标记为 late 来解决这个问题:
1 | late String description; |
当一个 late 修饰的变量在声明时就指定了初始化方法,那么内容会在第一次使用变量时运行初始化。
终值和常量
如果你不打算更改一个变量,可以使用 final 或 const 修饰它,而不是使用 var 或作为类型附加。一个 final 变量只能设置一次,const 变量是编译时常量。(const 常量隐式包含了 final。)
下面是创建和设置 final 变量的示例:
1 | final name = 'Bob'; // Without a type annotation |
你不能修改 final 变量的值:
1 | name = 'Alice'; // Error: a final variable can only be set once. |
请使用 const 修饰 编译时常量 的变量。如果 const 变量位于类级别,请将其标记为 static const(静态常量)。
类型
内置类型有:
Numbers(int, double)Strings(String)Booleans(bool)Records((value1, value2))Lists(List, also known as arrays)Sets(Set)Maps(Map)Runes(Runes)Symbols(Symbol)The value null(Null)
常见类型就不说了,看一下Records类型:
Records在dart3.0及以上版本可用。Records是不可变的,只有getter而没有setter
语法:以逗号分隔的命名字段或位置字段列表,用小括号包裹:
1 | var record = ('first', a: 2, b: true, 'last'); |
dart中范型的用法与TypeScript比较像。
typedef可以定义类型别名,比如:
1 | // 定义类型别名IntList |
类型别名也可以使用范型:
1 | typedef ListMapper<X> = Map<X, List<X>>; |
函数方法
Dart是面向对象的语言,所以即使是函数也是对象,并且有一个类型Function。这意味着函数可以分配给变量或作为参数传递给其他函数。
1 | // 函数示例 |
=> expr 语法是 { return expr; } 的简写。=> 表示法有时称为箭头语法。
参数
一个函数可以具有任意数量的必需位置参数。这些参数后面可以跟着命名参数或可选的位置参数(但不能同时跟这两个参数)。
在向函数传递参数或定义函数参数时,可以使用尾随逗号。
命名参数
命名参数是可选的,除非它们被显式标记为required.
定义函数时,请使用 {param1, param2, …} 指定命名参数。如果未提供默认值或将命名参数标记为required,则其类型必须为 null,因为其默认值为 null:
1 | void enableFlags({bool? bold, bool? hidden}) {...} |
可选位置参数
将一组函数参数包装在 [] 中将它们标记为可选位置参数。如果未提供默认值,则其类型必须可为 null,因为其默认值将为 null:
1 | String say(String from, String msg, [String? device]) { |
main函数
每个应用都必须有一个顶级 main() 函数,该函数用作应用的入口点。main() 函数返回 void 并具有可选的 List<String> 参数参数。
下面是一个简单的 main() 函数:
1 | void main() { |
下面是接受参数的命令行应用的 main() 函数示例:
1 | // Run the app like this: dart args.dart 1 test |
匿名函数
匿名函数看起来类似于命名函数,即零个或多个参数,括号之间用逗号和可选的类型注释分隔。
下面的代码块包含函数的主体:
1 | ([[Type] param1[, …]]) { codeBlock;}; |
流程控制
if, for, while, break, continue等关键字及用法,与其他语言基本一致,不过多赘述,给几个示例:
标准for循环迭代:
1 | for (int i = 0; i < 5; i++) { |
forEach:
1 | List<int> coll = [1, 2, 3]; |
while和do-chile
1 | while(条件){ |
if语句:
1 | if(bool) { |
switch语句
1 | switch(expr) { |
类和对象
Dart是一种面向对象的语言,具有类和基于mixin的继承。每个对象都是一个类的实例,除Null之外的所有类都派生自Object。基于mixin的继承意味着尽管每个类(除了顶层类Object)只有一个超类,但类体可以在多个类层次结构中重用。
类成员
对象的成员由方法和实例变量组成
1 | var p = Point(2, 2); |
可以使用?.调用来避免左侧操作数为null时发生异常:
1 | var x = p?.x; |
构造函数
创建与类名同名的函数来声明构造函数,初始化形式参数以实例化任何实例变量。
1 | class Point { |
使用命名构造函数为一个类实现多个构造函数,或提供额外的清晰度:
1 | const double xOrigin = 0; |
请记住,构造函数不是继承的,这意味着超类的命名构造函数不是由子类继承的。如果要使用超类中定义的命名构造函数创建子类,则必须在子类中实现该构造函数。
您可以使用构造函数创建对象。构造函数名称可以是 ClassName 或 ClassName.identifier。例如,以下代码使用 Point和 Point.fromJson() 构造函数创建 Point() 对象:
1 | var p1 = Point(2, 2); |
以下代码具有相同的效果,但在构造函数名称之前使用可选的 new 关键字:
1 | var p1 = new Point(2, 2); |
静态变量
使用 static 关键字实现类范围的变量和方法。
静态变量(类变量)对于类范围的状态和常量很有用:
1 | class Queue { |
静态变量在使用之前不会初始化。
并发
Dart 库充满了返回 Future 或 Stream 对象的函数。这些函数是异步的:它们在设置可能耗时的操作(如 I/O)后返回,而无需等待该操作完成。
async 和 await 关键字支持异步编程,允许您编写类似于同步代码的异步代码。
当您需要完成的 Future 的结果时,您有两种选择:
- 使用
async和await - 使用 Future API
使用 async 和 await 的代码是异步的,但它看起来很像同步代码。例如,下面是一些使用 await 等待异步函数结果的代码:
1 | await lookUpVersion(); |
若要使用 await,代码必须位于async步函数中,即标记为async的函数:
1 | Future<void> checkVersion() async { |
使用 try、catch 和 finally 处理使用 await 的代码中的错误和清理:
1 | try { |
您可以在async函数中多次使用 await。
补充
补充一些注意事项,如果你是从其他语言转过来的,可能用得上:
在dart中可以使用!做类型断言不为空,比如data.name!
类中的方法以_开头表示不对外暴露,仅内部可访问,这个内部指的是文件,即这个文件中都可以访问,但是其他文件不可以访问。
dart中有类似于js中模板字符串的用法:
- 渲染变量:
'xxx $variable xxx'- 直接用$变量即可 - 进行运算:
'xxx ${var1 + var2} xxx'- 用${表达式}渲染
函数中的位置参数如果不标记为可选都是必传的,通过{}包裹的命名参数如果不使用required修饰则都是可选的
dart中构造函数默认不能自定义返回值,如果需要可以使用工厂构造,比如Color.fromRGBO这些就是:
1 | class Chat { |
dart中的类实例才可以a.b这样访问属性,但是在map中需要a[b]这样访问。
这是一些dart的快速上手内容,了解了之后,就可以直接写起来了,在写的过程中,才能慢慢深入了解dart这门语言。