当前位置:网站首页 > 编程语言 > 正文

dart编程语言pdf下载_fortran编程语言

Dart编程语言概览

一个简单的Dart程序:

  • 注释,单行、多行
  • 数据类型、字面量、输出方式
  • 字符串插值
  • main()函数:特定的顶级函数
  • 定义变量var:通过这种方式定义变量不需要指定变量类型
// 定义一个函数 printInteger(int aNumber) { 
    // 打印 print('The number is $aNumber)'); } // 应用从这里开始 main() { 
    var number = 42; printInteger(number); } 

重要的概念:

  • 一切皆对象,所有对象都有对应的一个的实例;无论数字、函数和null都是对象;所有对象都继承自Object类;
  • Dart是强类型语言,但可以推断类型;如果要明确说明不需要任何类型,需要使用特殊类型dynamic动态类型;
  • Dart支持泛型,如List<int>整数列表、List<dynamic>任何类型的对象列表;
  • Dart对函数的支持:
    • 支持顶级函数main()
    • 绑定在类上——静态函数
    • 绑定在对象上——实例函数
    • 支持函数内创建函数(嵌套或 局部函数)
  • Dart对变量的支持:
    • 支持顶级变量
    • 绑定在类上——静态变量
    • 绑定在对象上——实例变量(字段/属性)
  • Dart没有关键字public/protected/private,如果标识符以下划线_开头,则它相对于库是私有的;
  • Dart表达式(运行时有值),语句(运行时无值);condition?expr1:expr2值可能是二者之一,if-else语句没有值;语句可以包含表达式,但是表达式不能直接包含语句;
  • Dart工具提示两种类型问题:警告 和 错误(编译时错误会阻止代码执行 或 运行时错误会导致代码在执行过程中引发异常);

Dart关键字解析:

  • abstract:定义 抽象类 — 抽象类不能实例化;抽象类通常用来定义接口,以及部分实现。 如果希望抽象类能够被实例化,那么可以通过定义一个 工厂构造函数 来实现;
// 这个类被定义为抽象类, // 所以不能被实例化。 abstract class AbstractContainer { 
    // 定义构造行数,字段,方法... void updateChildren(); // 抽象方法。 } 
  • asasisis!运算符用于在运行时处理类型检查;
    • 例如, obj is Object 总是 true。 但是只有 obj 实现了 T 的接口时, obj is T 才是 true。
    • 使用 as 运算符将对象强制转换为特定类型;
if (emp is Person) { 
    (emp as Person).firstName = 'Bob'; } 
  • assert:如果 assert 语句中的布尔条件为 false , 那么正常的程序执行流程会被中断
    • 例如,assert(urlString.startsWith('https'), 'URL ($urlString) should start with "https".');
    • assert 语句只在开发环境中有效, 在生产环境是无效的;
    • 断言失败,会抛出异常 (AssertionError);
  • asyncawait
    • Dart 库中包含许多返回 Future 或 Stream 对象的函数. 这些函数在设置完耗时任务(例如 I/O 曹组)后, 就立即返回了,不会等待耗任务完成。 使用 async 和 await 关键字实现异步编程。 可以让你像编写同步代码一样实现异步操作。
    • 要使用 await , 代码必须在 异步函数(使用 async 标记的函数)中;
    • 使用 try, catch, 和 finally 来处理代码中使用 await 导致的错误。
    • 在一个异步函数中可以多次使用 await
    • 在 await 表达式 中, 表达式 的值通常是一个 Future 对象; 如果不是,这是表达式的值会被自动包装成一个 Future 对象。
Future checkVersion() async { 
    var version = await lookUpVersion(); // Do something with version } try { 
    version = await lookUpVersion(); } catch (e) { 
    // React to inability to look up the version } var entrypoint = await findEntrypoint(); var exitCode = await runExecutable(entrypoint, args); await flushThenExit(exitCode); 
  • breakcontinue
    • 使用 break 停止程序循环,使用 continue 跳转到下一次迭代;
    • 如果对象实现了 Iterable 接口 (例如,list 或者 set)。 那么示例完全可以用另一种方式来实现:
while (true) { 
    if (shutDownRequested()) break; processIncomingRequests(); } for (int i = 0; i < candidates.length; i++) { 
    var candidate = candidates[i]; if (candidate.yearsExperience < 5) { 
    continue; } candidate.interview(); } candidates .where((c) => c.yearsExperience >= 5) .forEach((c) => c.interview()); 
  • caseswitchdefault
    • 在Dart中switch语句使用==比较比较整数,字符串,或者编译时常量
    • 比较的对象必须都是同一个类的实例(并且不可以是子类), 类必须没有对 == 重写。
    • 枚举类型 可以用于 switch 语句
    • 在 case 语句中,每个非空的 case 语句结尾需要跟一个 break 语句;除 break 以外,还有可以使用 continue, throw,者 return。
    • Dart 支持空 case 语句, 允许程序以 fall-through 的形式执行。
    • 在非空 case 中实现 fall-through 形式, 可以使用 continue 语句结合 lable 的方式实现
    • case 语句可以拥有局部变量, 这些局部变量只能在这个语句的作用域中可见。
var command = 'OPEN'; switch (command) { 
    case 'CLOSED': executeClosed(); // break; // 缺省break会报错 case 'PENDING': // executePending(); //但支持空case语句 // break; case 'APPROVED': executeApproved(); continue open; case 'DENIED': executeDenied(); break; open: case 'OPEN': executeOpen(); break; default: executeUnknown(); } 
  • catchfinallyrethrow:捕获异常可以避免异常继续传递(除非重新抛出( rethrow )异常)。 可以通过捕获异常的机会来处理该异常
    • 通过指定多个 catch 语句,可以处理可能抛出多种类型异常的代码。
    • catch 语句未指定类型, 则该语句可以处理任何类型的抛出对象
    • catch() 函数可以指定1到2个参数, 第一个参数为抛出的异常对象, 第二个为堆栈信息 ( 一个 StackTrace 对象 )。
    • 如果仅需要部分处理异常, 那么可以使用关键字 rethrow 将异常重新抛出。
    • 不管是否抛出异常, finally 中的代码都会被执行。 如果 没有用catch 匹配异常, 异常会在 finally 执行完成后,再次被抛出;任何匹配的 catch 执行完成后,再执行 finally ;
try { 
    breedMoreLlamas(); } on OutOfLlamasException { 
    // 一个特殊的异常 buyMoreLlamas(); } on Exception catch (e) { 
    // 其他任何异常 print('Unknown exception: $e'); } catch (e, s) { 
    // 没有指定的类型,处理所有异常 print('Something really unknown: $e'); rethrow; } finally { 
    // Always clean up, even if an exception is thrown. cleanLlamaStalls(); } 
  • throw
    • 高质量的生产环境代码通常会实现 Error 或 Exception 类型的异常抛出
// 抛出异常 throw FormatException('Expected at least 1 section'); // 抛出任意对象 throw 'Out of llamas!'; // 因为抛出异常是一个表达式, 所以可以在 => 语句中使用,也可以在其他使用表达式的地方抛出异常: void distanceTo(Point other) => throw UnimplementedError(); 
  • classthis:class 用于声明类;
    • 所有实例变量都生成隐式 getter 方法。 非 final 的实例变量同样会生成隐式 setter 方法
    • 构造函数中,使用 this 关键字引用当前实例;仅当存在命名冲突时,使用 this 关键字。 否则,按照 Dart 风格应该省略 this ;(通常模式下,会将构造函数传入的参数的值赋值给对应的实例变量)
class Point { 
    num x; // 声明示例变量 x,初始值为 null 。 num y; // 声明示例变量 y,初始值为 null 。 num z = 0; // 声明示例变量 z,初始值为 0 。 // 生成构造函数 Point(num x, num y) { 
    // 还有更好的方式来实现下面代码,敬请关注。 this.x = x; this.y = y; } } 
  • constfinal
    • 使用过程中从来不会被修改的变量, 可以使用 final 或 const, 而不是 var 或者其他类型
    • Final 变量的值只能被设置一次;Const 变量在编译时就已经固定 (Const 变量 是隐式 Final 的类型.)
    • 实例变量可以是 final 类型但不能是 const 类型。
    • 如果 Const 变量是类级别的,需要标记为 static const
    • Const 关键字不仅可以用于声明常量变量,还可以用来创建常量值(const关键字在声明常量构造函数时还有应用,参考关键字new的描述)
// 声明常量变量 const bar = ; // 创建常量值 var foo = const []; 
  • deferred
    • Deferred loading (也称之为 lazy loading) 可以让应用在需要的时候再加载库
    • 常用场景:减少 APP 的启动时间。执行 A/B 测试,例如 尝试各种算法的 不同实现。加载很少使用的功能,例如可选的屏幕和对话框。
    • 延迟加载库的常量在导入的时候是不可用的,在导入文件的时候也无法使用延迟库中的类型;
// 要延迟加载一个库,需要先使用 deferred as 来导入 import 'package:greetings/hello.dart' deferred as hello; // 当需要使用的时候,使用库标识符调用 loadLibrary() 函数来加载库: Future greet() async { 
    // 可以多次调用 loadLibrary() 函数。但是该库只是载入一次 await hello.loadLibrary(); hello.printGreeting(); } 
  • dodo-while
while (!isDone()) { 
    doSomething(); } do { 
    printLine(); } while (!atEndOfPage()); 
  • dynamic:动态的数据类型
  • else if:和 JavaScript 不同, Dart 的判断条件必须是布尔值,不能是其他类型
if (isRaining()) { 
    you.bringRainCoat(); } else if (isSnowing()) { 
    you.wearJacket(); } else { 
    car.putTopDown(); } 
  • enum:枚举类型也称为 enumerations 或 enums , 是一种特殊的类,用于表示数量固定的常量值
    • 枚举中的每个值都有一个 index getter 方法, 该方法返回值所在枚举类型定义中的位置(从 0 开始)
    • 使用枚举的 values 常量, 获取所有枚举值列表( list )
    • 可以在 switch 语句 中使用枚举, 如果不处理所有枚举值,会收到警告
    • 枚举不能被子类化,混合或实现
    • 枚举不能被显式实例化
enum Color { 
    red, green, blue } assert(Color.red.index == 0); List<Color> colors = Color.values; assert(colors[2] == Color.blue); 
  • export
    • 库代码位于lib目录下,对其他包是公开的。您可以根据需要在lib下创建任何层次结构。按照惯例,实现代码放在lib/src下。lib/src下的代码被认为是私有的;其他包永远不需要导入src/…要使lib/src下的api公开,可以从直接位于lib下的文件导出lib/src文件;
// 目录结构 - src - cascade.dart - ... - shelf.dart - shelf_io.dart // shelf.dart, exports several files from lib/src: export 'src/cascade.dart'; export ... 
  • extendssuper:使用 extends 关键字来创建子类, 使用 super 关键字来引用父类
class Television { 
    void turnOn() { 
    _illuminateDisplay(); _activateIrSensor(); } // ··· } class SmartTelevision extends Television { 
    void turnOn() { 
    super.turnOn(); _bootNetworkInterface(); _initializeMemory(); _upgradeApps(); } // ··· } 
  • factory:工厂构造函数
    • 当执行构造函数并不总是创建这个类的一个新实例时,则使用 factory 关键字。
    • 一个工厂构造函数可能会返回一个 cache 中的实例, 或者可能返回一个子类的实例。
    • 工厂构造函数无法访问 this。
class Logger { 
    final String name; bool mute = false; // 从命名的 _ 可以知, // _cache 是私有属性。 static final Map<String, Logger> _cache = <String, Logger>{ 
   }; factory Logger(String name) { 
    if (_cache.containsKey(name)) { 
    return _cache[name]; } else { 
    final logger = Logger._internal(name); _cache[name] = logger; return logger; } } Logger._internal(this.name); void log(String msg) { 
    if (!mute) print(msg); } } // 工厂构造函的调用方式与其他构造函数一样  var logger = Logger('UI'); logger.log('Button clicked'); 
  • falsetrue
  • for
    • 闭包在 Dart 的 for 循环中会捕获循环的 index 索引值, 来避免 JavaScript 中常见的陷阱
    • 如果要迭代一个实现了 Iterable 接口的对象, 可以使用 forEach() 方法
    • 实现了 Iterable 的类(比如, List 和 Set)同样也支持使用 for-in 进行迭代操作 iteration
var message = StringBuffer('Dart is fun'); for (var i = 0; i < 5; i++) { 
    message.write('!'); } var callbacks = []; for (var i = 0; i < 2; i++) { 
    // 输出的是 0 和 1。 但是示例中的代码在 JavaScript 中会连续输出两个 2  callbacks.add(() => print(i)); } // 如果不需要使用当前计数值, 使用 forEach() 是非常棒的选择; callbacks.forEach((c) => c()); var collection = [0, 1, 2]; for (var x in collection) { 
    print(x); // 0 1 2 } 
  • Function
    • Dart 是一门真正面向对象的语言, 甚至其中的函数也是对象,并且有它的类型 Function;
    • 这也意味着函数可以被赋值给变量或者作为参数传递给其他函数。
    • 箭头 语法=> expr; 语法是 { return expr; } 的简写;
    • 在箭头 (=>) 和分号 (;) 之间只能使用一个 表达式 ,不能是 语句
    • 函数有两种参数类型: required 和 optional。 required 类型参数在参数最前面, 随后是 optional 类型参数。 命名的可选参数也可以标记为 “@required
    • 可选参数可以是命名参数或者位置参数,但一个参数只能选择其中一种方式修饰。
  • 命名参数 & 位置参数:
    • 默认值只能是编译时常量。 如果没有提供默认值,则默认值为 null。
    • list 或 map 可以作为默认值传递;
// 位置参数声明方式 bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null; // 位置参数的调用方式 isNoble(100); // 命名参数声明方式 void enableFlags({ 
   bool bold, bool hidden= false}) { 
   ...} // 命名参数的调用方式 enableFlags(bold: true, hidden: false); // Flutter 创建实例的表达式可能很复杂, 因此窗口小部件构造函数仅使用命名参数 const Scrollbar({ 
   Key key, @required Widget child}) // 位置可选参数 String say(String from, String msg, [String device]) { 
    var result = '$from says $msg'; if (device != null) { 
    result = '$result with a $device'; } return result; } // list 或 map 可以作为默认值传递 void doStuff( { 
   List<int> list = const [1, 2, 3], Map<String, String> gifts = const { 
    'first': 'paper', 'second': 'cotton', 'third': 'leather' }}) { 
    print('list: $list'); print('gifts: $gifts'); } 

Required 被定义在 meta package。 无论是直接引入(import) package:meta/meta.dart ,或者引入了其他 package,而这个 package 输出(export)了 meta,比如 Flutter 的 package:flutter/material.dart

  • implements:隐式接口
    • 每个类都隐式的定义了一个接口,接口包含了该类所有的实例成员及其实现的接口。 如果要创建一个 A 类,A 要支持 B 类的 API ,但是不需要继承 B 的实现, 那么可以通过 A 实现 B 的接口。
    • 一个类可以通过 implements 关键字来实现一个或者多个接口, 并实现每个接口要求的 API。
// 一个类对应一个 隐式的接口Person class Person { 
    // 包含在接口里,但只在当前库中可见。 final _name; // 不包含在接口里,因为这是一个构造函数。 Person(this._name); // 包含在接口里。 String greet(String who) => 'Hello, $who. I am $_name.'; } // person 接口的实现。 class Impostor implements Person { 
    get _name => ''; String greet(String who) => 'Hi $who. Do you know who I am?'; } // 调用 String greetBob(Person person) => person.greet('Bob'); void main() { 
    print(greetBob(Person('Kathy'))); print(greetBob(Impostor())); } // 实现多个接口 class Point implements Comparable, Location { 
   ...} 
  • getset
    • Getter 和 Setter 是用于对象属性读和写的特殊方法
    • 使用 get 和 set 关键字实现 Getter 和 Setter ,能够为实例创建额外的属性。
class Rectangle { 
    num left, top, width, height; Rectangle(this.left, this.top, this.width, this.height); // 定义两个计算属性: right 和 bottom。 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); assert(rect.left == 3); rect.right = 12; assert(rect.left == -8); } 
  • import:通过 import 指定一个库命名空间中的内如如何在另一个库中使用
    • import 参数只需要一个指向库的 URI(URI 代表统一资源标识符)
    • 对于内置库,URI 拥有自己特殊的dart: 方案
    • 对于其他的库,使用系统文件路径或者 package: 方案
    • package: 方案指定由包管理器(如 pub 工具)提供的库
    • 如果导入两个存在冲突标识符的库, 则可以为这两个库,或者其中一个指定前缀
// Dart Web应用程序通常使用 dart:html 库,它们可以像这样导入: import 'dart:html'; // 指定库前缀 import 'package:lib1/lib1.dart'; import 'package:lib2/lib2.dart' as lib2; // 使用 lib1 中的 Element。 Element element1 = Element(); // 使用 lib2 中的 Element。 lib2.Element element2 = lib2.Element(); 
  • hideshow:如果你只使用库的一部分功能,则可以选择需要导入的 内容
// Import only foo. import 'package:lib1/lib1.dart' show foo; // Import all names EXCEPT foo. import 'package:lib2/lib2.dart' hide foo; 
  • in
    • for-in 进行迭代操作
  • interface
    • Yes. The interface keyword was removed from Dart. Instead all classes have implicit interfaces. So if you want to define an interface you can use an abstract class instead.
  • library:库和可见性
    • import 和 library 指令可以用来创建一个模块化的,可共享的代码库。
    • 库不仅提供了 API ,而且对代码起到了封装的作用: 以下划线 (_) 开头的标识符仅在库内可见。
    • 每个 Dart 应用程序都是一个库 ,虽然没有使用 library 指令
    • 库可以通过包(Package)来分发,pub(集成在SDK中的包管理器)
  • mixinwith
    • Dart 是一种基于类和 mixin 继承机制的面向对象的语言;
    • Mixin 提供了复用类代码的一种途径, 复用的类可以在不同层级,之间可以不存在继承关系。
    • 通过 with 后面跟一个或多个混入的名称,来 使用 Mixin
    • 通过创建一个继承自 Object 且没有构造函数的类,来 实现 一个 Mixin 。如果 Mixin 不希望作为常规类被使用,使用关键字 mixin 替换 class;
    • 指定只有某些类型可以使用的 Mixin - 比如, Mixin 可以调用 Mixin 自身没有定义的方法 - 使用 on 来指定可以使用 Mixin 的父类类型;
// 通过 with 后面跟一个或多个混入的名称,来 使用 Mixin  class Maestro extends Person with Musical, Aggressive, Demented { 
    Maestro(String maestroName) { 
    name = maestroName; canConduct = true; } } // Mixin mixin Musical { 
    bool canPlayPiano = false; bool canCompose = false; bool canConduct = false; void entertainMe() { 
    if (canPlayPiano) { 
    print('Playing piano'); } else if (canConduct) { 
    print('Waving hands'); } else { 
    print('Humming to self'); } } } // Musician是一个类, MusicalPerformer这个mixin 通过on关键字 就可以调用类的方法 mixin MusicalPerformer on Musician { 
    // ··· } 

mixin 关键字在 Dart 2.1 中被引用支持

  • new
    • 构造函数 创建对象。 构造函数的名字可以是 ClassName 或者 ClassName.identifier(这可能是一个工厂函数);
    • 构造函数前面的的 new 关键字是可选的
    • 一些类提供了常量构造函数。 使用常量构造函数,在构造函数名之前加 const 关键字,来创建编译时常量;
    • 在 常量上下文 中, 构造函数或者字面量前的 const 可以省略;
// 例如, 以下代码使用 Point 和 Point.fromJson() 构造函数创建 Point 对象: var p1 = Point(2, 2); var p2 = Point.fromJson({ 
   'x': 1, 'y': 2}); var p1 = new Point(2, 2); var p2 = new Point.fromJson({ 
   'x': 1, 'y': 2}); // 常量构造函数 var p = const ImmutablePoint(2, 2); // 构造两个相同的编译时常量会产生一个唯一的, 标准的实例 var a = const ImmutablePoint(1, 1); var b = const ImmutablePoint(1, 1); assert(identical(a, b)); // 它们是同一个实例。 // 这里有很多的 const 关键字。 const pointAndLine = const { 
    'point': const [const ImmutablePoint(0, 0)], 'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)], }; // 保留第一个 const 关键字,其余的全部省略: // 仅有一个 const ,由该 const 建立常量上下文。 const pointAndLine = { 
    'point': [ImmutablePoint(0, 0)], 'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)], }; 
  • null
    • 未初始化的变量默认值是 null,即使变量是数字 类型默认值也是 null,因为在 Dart 中一切都是对象,数字类型 也不例外;
int lineCount; // 在生产环境代码中 assert() 函数会被忽略,不会被调用。 在开发过程中, assert(condition) 会在非 true 的条件下抛出异常 assert(lineCount == null); 
  • on
    • 在捕获异常try-catch语句中,使用on来指定异常类型,使用catch来捕获异常对象;
    • mixin声明中,指定只有某些类型可以使用这个mixin
  • operator:操作符
    • 可以被重写的操作运算符:<+|[]>/^[]=<=~/&~>=*<<==%>>
    • 如果要重写 == 操作符,需要重写对象的 hashCode getter 方法。

你可能会被提示 != 运算符为非可重载运算符。 因为 e1 != e2 表达式仅仅是 !(e1 == e2) 的语法糖。

// 重写 + - class Vector { 
    final int x, y; Vector(this.x, this.y); Vector operator +(Vector v) => Vector(x + v.x, y + v.y); Vector operator -(Vector v) => Vector(x - v.x, y - v.y); // 运算符 == 和 hashCode 部分没有列出。 有关详情,请参考下面的代码段。 // ··· } void main() { 
    final v = Vector(2, 3); final w = Vector(2, 2); assert(v + w == Vector(4, 5)); assert(v - w == Vector(0, 1)); } 
// 重写 == 操作符 class Person { 
    final String firstName, lastName; Person(this.firstName, this.lastName); // 重写 hashCode,实现策略源于 Effective Java, // 第11章。 @override int get hashCode { 
    int result = 17; result = 37 * result + firstName.hashCode; result = 37 * result + lastName.hashCode; return result; } // 如果重写了 hashCode,通常应该从新实现 == 操作符。 @override bool operator ==(dynamic other) { 
    if (other is! Person) return false; Person person = other; return (person.firstName == firstName && person.lastName == lastName); } } void main() { 
    var p1 = Person('Bob', 'Smith'); var p2 = Person('Bob', 'Smith'); var p3 = 'not a person'; assert(p1.hashCode == p2.hashCode); assert(p1 == p2); assert(p1 != p3); } 
  • part
    • You may have heard of the part directive, which allows you to split a library into multiple Dart files. We recommend that you avoid using part and create mini libraries instead.
  • return:返回函数返回值;
  • static:使用 static关键字实现类范围的变量和方法
    • 静态变量(类变量)对于类级别的状态是非常有用的;
    • 静态变量只到它们被使用的时候才会初始化;
    • 静态方法(类方法)不能在实例上使用,因此它们不能访问 this
    • 静态函数可以当做编译时常量使用。 例如,可以将静态方法作为参数传递给常量构造函数。
// 静态变量 class Queue { 
    static const initialCapacity = 16; // ··· } void main() { 
    assert(Queue.initialCapacity == 16); } // 静态方法 import 'dart:math'; class Point { 
    num x, y; Point(this.x, this.y); static num distanceBetween(Point a, Point b) { 
    var dx = a.x - b.x; var dy = a.y - b.y; return sqrt(dx * dx + dy * dy); } } void main() { 
    var a = Point(2, 2); var b = Point(4, 4); var distance = Point.distanceBetween(a, b); assert(2.8 < distance && distance < 2.9); print(distance); } 

对于常见或广泛使用的工具和函数, 应该考虑使用顶级函数而不是静态方法;

代码准守风格推荐指南中的命名规则, 使用 lowerCamelCase 来命名常量。

  • sync yieldsync*async*):
    • 当您需要延迟生成( lazily produce )一系列值时, 可以考虑使用_生成器函数_。 Dart 内置支持两种生成器函数:
      • Synchronous 生成器: 返回一个 Iterable 对象
      • Asynchronous 生成器: 返回一个 Stream 对象
// 通过在函数体标记 sync*, 可以实现一个同步生成器函数。 使用 yield 语句来传递值 Iterable<int> naturalsTo(int n) sync* { 
    int k = 0; while (k < n) yield k++; } // 通过在函数体标记 async*, 可以实现一个异步生成器函数。 使用 yield 语句来传递值: Stream<int> asynchronousNaturalsTo(int n) async* { 
    int k = 0; while (k < n) yield k++; } // 如果生成器是递归的,可以使用 yield* 来提高其性能: Iterable<int> naturalsDownFrom(int n) sync* { 
    if (n > 0) { 
    yield* naturalsDownFrom(n - 1); } else { 
    yield n; } } 
  • typedef
    • 在 Dart 中,函数也是对象,就想字符和数字对象一样。 使用 typedef ,或者 function-type alias 为函数起一个别名, 别名可以用来声明字段及返回值类型。 当函数类型分配给变量时,typedef会保留类型信息。
// 未使用typedef class SortedCollection { 
    Function compare; SortedCollection(int f(Object a, Object b)) { 
    compare = f; } } // Initial, broken implementation. // broken ? int sort(Object a, Object b) => 0; void main() { 
    SortedCollection coll = SortedCollection(sort); // 虽然知道 compare 是函数, // 但是函数是什么类型 ? // 当把 f 赋值给 compare 的时候,类型信息丢失了。 f 的类型是 (Object, Object) → int (这里 → 代表返回值类型), 但是 compare 得到的类型是 Function assert(coll.compare is Function); } 

目前,typedefs 只能使用在函数类型上

// 使用typedef为函数起一个别名 typedef Compare = int Function(Object a, Object b); class SortedCollection { 
    Compare compare; SortedCollection(this.compare); } // Initial, broken implementation. int sort(Object a, Object b) => 0; void main() { 
    SortedCollection coll = SortedCollection(sort); assert(coll.compare is Function); assert(coll.compare is Compare); } // 判断任意函数的类型 typedef Compare<T> = int Function(T a, T b); int sort(int a, int b) => a - b; void main() { 
    assert(sort is Compare<int>); // True! } 
  • var:创建一个变量,初始化之后,变量仅存储对象引用;
  • void:函数无返回值的类型描述;如果不是void声明的函数返回值类型,那么函数就一定有返回值,实际上所有函数都会返回一个值。 如果没有明确指定返回值, 函数体会被隐式的添加 return null; 语句;

Dart 内建数据类型

支持的内建类型:

  • Number
  • String
  • Boolean
  • List(也被称为Array)
  • Map
  • Set
  • Rune(用于在字符串中表示Unicode字符)
  • Symbol

这些类型都可以被初始化为字面量;

因为在 Dart 所有的变量终究是一个对象(一个类的实例), 所以变量可以使用 构造涵数 进行初始化。 一些内建类型拥有自己的构造函数。 例如, 通过 Map() 来构造一个 map 变量。

几个参考链接,便于使用查阅:

  • Dart List操作
  • Dart Map操作
  • Dart Set文档
  • Dart 集合 API

Number

Dart 语言的 Number 有两种类型:

  • int:整数值不大于64位, 具体取决于平台。
  • double:64位(双精度)浮点数,依据 IEEE 754 标准

intdouble 都是 num. 的亚类型

  • num 类型包括基本运算 +-/, 和 *, 以及 abs()ceil(), 和 floor(), 等函数方法。
  • 如果 num 及其亚类型找不到你想要的方法, 尝试查找使用 dart:math 库。
  • 按位运算符,例如»,定义在 int 类中。int 特有的传统按位运算操作,移位(<<, >>),按位与(&)以及 按位或(|)。
var x = 1; var hex = 0xDEADBEEF; var y = 1.1; var exponents = 1.42e5; // 从 Dart 2.1 开始,必要的时候 int 字面量会自动转换成 double 类型 double z = 1; // 相当于 double z = 1.0 assert((3 << 1) == 6); // 0011 << 1 == 0110 assert((3 >> 1) == 1); // 0011 >> 1 == 0001 assert((3 | 4) == 7); // 0011 | 0100 == 0111 // 数字类型字面量是编译时常量。 在算术表达式中,只要参与计算的因子是编译时常量, 那么算术表达式的结果也是编译时常量 const msPerSecond = 1000; const secondsUntilRetry = 5; const msUntilRetry = secondsUntilRetry * msPerSecond; 

字符串 与 数字 的相互转换:

 // String -> int var one = int.parse('1'); assert(one == 1); // String -> double var onePointOne = double.parse('1.1'); assert(onePointOne == 1.1); // int -> String String oneAsString = 1.toString(); assert(oneAsString == '1'); // double -> String String piAsString = 3.14159.toStringAsFixed(2); assert(piAsString == '3.14'); 

String

Dart 字符串是一组 UTF-16 单元序列:

  • 字符串通过单引号或者双引号创建。
  • 字符串可以通过 ${expression} 的方式内嵌表达式,如果表达式是一个标识符,则 {} 可以省略,如$var
  • 在 Dart 中通过调用就对象的 toString() 方法来得到对象相应的字符串
  • 可以使用 + 运算符来把多个字符串连接为一个(把多个字面量字符串写在一起也可以实现字符串连接);
  • 使用连续三个单引号或者三个双引号实现多行字符串对象的创建:
  • 使用 r 前缀,可以创建 “原始 raw” 字符串:

== 运算符用来测试两个对象是否相等。 在字符串中,如果两个字符串包含了相同的编码序列,那么这两个字符串相等

一个编译时常量的字面量字符串中,如果存在插值表达式,表达式内容也是编译时常量, 那么该字符串依旧是编译时常量。 插入的常量值类型可以是 null,数值,字符串或布尔值:

// const 类型数据 const aConstNum = 0; const aConstBool = true; const aConstString = 'a constant string'; // 非 const 类型数据 var aNum = 0; var aBool = true; var aString = 'a string'; const aConstList = [1, 2, 3]; const validConstString = '$aConstNum $aConstBool $aConstString'; //const 类型数据 // const invalidConstString = '$aNum $aBool $aString $aConstList'; //非 const 类型数据 

字符常用方法和正则表达式:

  • 使用正则表达式 (RegExp 对象) 可以在字符串内搜索和替换部分字符串
  • String 定义了例如 split()contains()startsWith()endsWith() 等方法
  • 字符串是不可变的对象,也就是说字符串可以创建但是不能被修改,例如,方法 replaceAll() 返回一个新字符串, 并没有改变原始字符串;
  • 具体参看如下代码示例;
// 检查一个字符串是否包含另一个字符串。 assert('Never odd or even'.contains('odd')); // 一个字符串是否以另一个字符串为开头? assert('Never odd or even'.startsWith('Never')); // 一个字符串是否以另一个字符串为结尾? assert('Never odd or even'.endsWith('even')); // 查找一个字符串在另一个字符串中的位置。 assert('Never odd or even'.indexOf('odd') == 6); // 抓取一个子字符串。 assert('Never odd or even'.substring(6, 9) == 'odd'); // 使用字符串模式分割字符串。 var parts = 'structured web apps'.split(' '); assert(parts.length == 3); assert(parts[0] == 'structured'); // 通过下标获取 UTF-16 编码单元(编码单元作为字符串)。 assert('Never odd or even'[0] == 'N'); // 使用 split() 传入一个空字符串参数, // 得到一个所有字符的 list 集合; // 有助于字符迭代。 for (var char in 'hello'.split('')) { 
    print(char); } // 获取一个字符串的所有 UTF-16 编码单元。 var codeUnitList = 'Never odd or even'.codeUnits.toList(); assert(codeUnitList[0] == 78); // 转换为首字母大写。 assert('structured web apps'.toUpperCase() == 'STRUCTURED WEB APPS'); // 转换为首字母小写。 assert('STRUCTURED WEB APPS'.toLowerCase() == 'structured web apps'); // Trim a string. assert(' hello '.trim() == 'hello'); // 检查字符串是否为空。 assert(''.isEmpty); // 空格字符串不是空字符串。 assert(' '.isNotEmpty); // 替换部分字符串 var greetingTemplate = 'Hello, NAME!'; var greeting = greetingTemplate.replaceAll(RegExp('NAME'), 'Bob'); // greetingTemplate 没有改变。 assert(greeting != greetingTemplate); // 要以代码方式生成字符串,可以使用 StringBuffer // 在调用 toString() 之前, StringBuffer 不会生成新字符串对象。 writeAll() 的第二个参数为可选参数,用来指定分隔符, 本例中使用空格作为分隔符。 var sb = StringBuffer(); sb ..write('Use a StringBuffer for ') ..writeAll(['efficient', 'string', 'creation'], ' ') ..write('.'); // .. 是 级联运算符 var fullString = sb.toString(); assert(fullString == 'Use a StringBuffer for efficient string creation.'); // RegExp类提供与JavaScript正则表达式相同的功能。 使用正则表达式可以对字符串进行高效搜索和模式匹配。 // 下面正则表达式用于匹配一个或多个数字。 var numbers = RegExp(r'\d+'); var allCharacters = 'llamas live fifteen to twenty years'; var someDigits = 'llamas live 15 to 20 years'; // contains() 能够使用正则表达式。 assert(!allCharacters.contains(numbers)); assert(someDigits.contains(numbers)); // 替换所有匹配对象为另一个字符串。 var exedOut = someDigits.replaceAll(numbers, 'XX'); assert(exedOut == 'llamas live XX to XX years'); // 你也可以直接使用RegExp类。 Match 类提供对正则表达式匹配对象的访问。 var numbers = RegExp(r'\d+'); var someDigits = 'llamas live 15 to 20 years'; // 检查正则表达式是否在字符串中匹配到对象。 assert(numbers.hasMatch(someDigits)); // 迭代所有匹配对象 for (var match in numbers.allMatches(someDigits)) { 
    print(match.group(0)); // 15, then 20 } 

Boolean

Dart 使用 bool 类型表示布尔值。 Dart 只有字面量 true and false 是布尔类型, 这两个对象都是编译时常量。

Dart 的类型安全意味着不能使用 if (nonbooleanValue) 或者 assert (nonbooleanValue)。 而是应该像下面这样,明确的进行值检查:

// 检查空字符串。 var fullName = ''; assert(fullName.isEmpty); // 检查 0 值。 var hitPoints = 0; assert(hitPoints <= 0); // 检查 null 值。 var unicorn; assert(unicorn == null); // 检查 NaN 。 var iMeantToDoThis = 0 / 0; assert(iMeantToDoThis.isNaN); 

List

在 Dart 中的 Array 就是 List 对象, 通常称之为 List 。

  • Dart 集合 API
  • var list = [1, 2, 3];
  • Lists 的下标索引从 0 开始,第一个元素的索引是 0。 list.length - 1 是最后一个元素的索引
var list = [1, 2, 3]; assert(list.length == 3); assert(list[1] == 2); list[1] = 1; assert(list[1] == 1); // 在 List 字面量之前添加 const 关键字,可以定义 List 类型的编译时常量 var constantList = const [1, 2, 3]; // constantList[1] = 1; // 取消注释会引起错误。 // 使用 List 构造函数。 var vegetables = List(); // 或者仅使用一个 list 字面量。 var fruits = ['apples', 'oranges']; // 添加一个元素到 list 对象。 fruits.add('kiwis'); // 添加多个元素到 list 对象。 fruits.addAll(['grapes', 'bananas']); // 获取 list 长度。 assert(fruits.length == 5); // 移除一个元素到 list 对象。 var appleIndex = fruits.indexOf('apples'); fruits.removeAt(appleIndex); assert(fruits.length == 4); // 移除多个元素到 list 对象。 fruits.clear(); assert(fruits.length == 0); // 使用 sort() 方法排序一个 list  // 下面示例中使用 compareTo() 函数, 该函数在 Comparable 中定义, 并被 String 类实现 var fruits = ['bananas', 'apples', 'oranges']; // 排序一个 list 。 fruits.sort((a, b) => a.compareTo(b)); assert(fruits[0] == 'apples'); // list 是参数化类型, 因此可以指定 list 应该包含的元素类型 // 这个 list 只能包含字符串类型。 var fruits = List<String>(); fruits.add('apples'); var fruit = fruits[0]; assert(fruit is String); // 产生静态分析警告,num 不是字符串类型。 fruits.add(5); // BAD: Throws exception in checked mode. 

Set

在 Dart 中 Set 是一个元素唯一且无需的集合。

  • Dart 为 Set 提供了 Set 字面量和 Set 类型
  • var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};,Dart 推断 halogens 类型为 Set 。如果尝试为它添加一个 错误类型的值,分析器或执行时会抛出错误;
  • 要创建一个空集,使用前面带有类型参数的 {} ,或者将 {} 赋值给 Set 类型的变量;

虽然 Set 类型 一直是 Dart 的核心部分, 但在 Dart2.2 中才引入了 Set 字面量 。

var names = <String>{ 
   }; // Set<String> names = {}; // 这样也是可以的。 // var names = {}; // 这样会创建一个 Map ,而不是 Set 这会创建一个类型为 Map<dynamic, dynamic> 的对象; var ingredients = Set(); ingredients.addAll(['gold', 'titanium', 'xenon']); assert(ingredients.length == 3); // 添加一个重复的元素是无效的。 ingredients.add('gold'); assert(ingredients.length == 3); // 从 set 中移除一个元素。 ingredients.remove('gold'); assert(ingredients.length == 2); // 在 Set 字面量前增加 const ,来创建一个编译时 Set 常量: final constantSet = const { 
    'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine', }; // 使用 contains() 和 containsAll() 来检查一个或多个元素是否在 set 中。 var ingredients = Set(); ingredients.addAll(['gold', 'titanium', 'xenon']); // 检查一个元素是否在该 set 中。 assert(ingredients.contains('titanium')); // 检查多个元素是否在该 set 中。 assert(ingredients.containsAll(['titanium', 'xenon'])); // 交集是另外两个 set 中的公共元素组成的 set  var ingredients = Set(); ingredients.addAll(['gold', 'titanium', 'xenon']); // 创建两个 set 的交集。 var nobleGases = Set.from(['xenon', 'argon']); var intersection = ingredients.intersection(nobleGases); assert(intersection.length == 1); assert(intersection.contains('xenon')); 

Map

通常来说, Map 是用来关联 keys 和 values 的对象:

  • keys 和 values 可以是任何类型的对象。
  • 在一个 Map 对象中一个 key 只能出现一次。 但是 value 可以出现多次。
  • Dart 中 Map 通过 Map 字面量 和 Map 类型来实现。
  • 如果 Map 中不包含所要查找的 key,那么 Map 返回 null
  • 使用 .length 函数获取当前 Map 中的 key-value 对数量
  • 创建 Map 类型运行时常量,要在 Map 字面量前加上关键字 const
  • 使用 remove() 方法从 map 中移除键值对
var gifts = { 
    // Key: Value 'first': 'partridge', 'second': 'turtledoves', 'fifth': 'golden rings' }; var nobleGases = { 
    2: 'helium', 10: 'neon', 18: 'argon', }; var gifts = Map(); gifts['first'] = 'partridge'; gifts['second'] = 'turtledoves'; gifts['fifth'] = 'golden rings'; var nobleGases = Map(); nobleGases[2] = 'helium'; nobleGases[10] = 'neon'; nobleGases[18] = 'argon'; 

上面代码,Dart 会将 gifts 的类型推断为 Map<String, String>, nobleGases 的类型推断为 Map<int, String> 。 如果尝试在上面的 map 中添加错误类型,那么分析器或者运行时会引发错误

// map 是参数化类型; // 可以指定一个 map 中 key 和 value 的类型。 var nobleGases = Map<int, String>(); //  var nobleGases = { 
   54: 'xenon'}; // 使用 key 检索 value 。 assert(nobleGases[54] == 'xenon'); // 检查 map 是否包含 key 。 assert(nobleGases.containsKey(54)); // 移除一个 key 及其 value。 nobleGases.remove(54); assert(!nobleGases.containsKey(54)); var hawaiianBeaches = { 
    'Oahu': ['Waikiki', 'Kailua', 'Waimanalo'], 'Big Island': ['Wailea Bay', 'Pololu Beach'], 'Kauai': ['Hanalei', 'Poipu'] }; // 获取的所有的 key 是一个无序集合 // (可迭代 list 对象)。 var keys = hawaiianBeaches.keys; assert(keys.length == 3); assert(Set.from(keys).contains('Oahu')); // 获取的所有的 value 是一个无序集合 // (可迭代 list 对象). var values = hawaiianBeaches.values; assert(values.length == 3); assert(values.any((v) => v.contains('Waikiki'))); assert(hawaiianBeaches.containsKey('Oahu')); assert(!hawaiianBeaches.containsKey('Florida')); 

公共集合方法

List, Set, 和 Map 共享许多集合中的常用功能。 其中一些常见功能由 Iterable 类定义, 这些函数由 List 和 Set 实现。

虽然Map没有实现 Iterable, 但可以使用 Map keys 和 values 属性从中获取 Iterable 对象。

var coffees = []; var teas = ['green', 'black', 'chamomile', 'earl grey']; assert(coffees.isEmpty); assert(teas.isNotEmpty); var teas = ['green', 'black', 'chamomile', 'earl grey']; teas.forEach((tea) => print('I drink $tea')); // 当在 map 对象上调用 `forEach() 方法时,函数必须带两个参数(key 和 value) hawaiianBeaches.forEach((k, v) { 
    print('I want to visit $k and swim at $v'); // 我想去瓦胡岛并且在 // [Waikiki, Kailua, Waimanalo]游泳, 等等。 }); var teas = ['green', 'black', 'chamomile', 'earl grey']; // map() 方法返回的对象是一个 懒求值(lazily evaluated)对象: 只有当访问对象里面的元素时,函数才会被调用。 var loudTeas = teas.map((tea) => tea.toUpperCase()); loudTeas.forEach(print); // 使用 map().toList() 或 map().toSet() , 可以强制在每个项目上立即调用函数。 var loudTeas = teas.map((tea) => tea.toUpperCase()).toList(); // 使用 Iterable 的 where() 方法可以获取所有匹配条件的元素。 使用 Iterable 的 any() 和 every() 方法可以检查部分或者所有元素是否匹配某个条件。 var teas = ['green', 'black', 'chamomile', 'earl grey']; // 洋甘菊不含咖啡因。 bool isDecaffeinated(String teaName) => teaName == 'chamomile'; // 使用 where() 来查找元素, // 这些元素在给定的函数中返回 true 。 var decaffeinatedTeas = teas.where((tea) => isDecaffeinated(tea)); // 或者 teas.where(isDecaffeinated) // 使用 any() 来检查集合中是否至少有一个元素满足条件。 assert(teas.any(isDecaffeinated)); // 使用 every() 来检查集合中是否所有元素满足条件。 assert(!teas.every(isDecaffeinated)); // 如果当且仅当该 key 不存在于 map 中,且要为这个 key 赋值, 可使用putIfAbsent()方法。 该方法需要一个方法返回这个 value  var teamAssignments = { 
   }; teamAssignments.putIfAbsent( 'Catcher', () => pickToughestKid()); assert(teamAssignments['Catcher'] != null); 

Rune

Rune 用来表示字符串中的 UTF-32 编码字符

Unicode 定义了一个全球的书写系统编码, 系统中使用的所有字母,数字和符号都对应唯一的数值编码。 由于 Dart 字符串是一系列 UTF-16 编码单元, 因此要在字符串中表示32位 Unicode 值需要特殊语法支持。

表示 Unicode 编码的常用方法是, \uXXXX, 这里 XXXX 是一个4位的16进制数。 例如,心形符号 () 是 \u2665。 对于特殊的非 4 个数值的情况, 把编码值放到大括号中即可。 例如,emoji 的笑脸 () 是 \u{1f600}

String 类有一些属性可以获得 rune 数据。 属性 codeUnitAt 和 codeUnit 返回16位编码数据。 属性 runes 获取字符串中的 Rune 。

下面是示例演示了 Rune 、 16-bit code units、 和 32-bit code points 之间的关系:

main() { 
    var clapping = '\u{1f44f}'; print(clapping); print(clapping.codeUnits); print(clapping.runes.toList()); Runes input = new Runes( '\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}'); print(new String.fromCharCodes(input)); } // console 👏 [55357, 56399] [] ♥ 😅 😎 👻 🖖 👍 

谨慎使用 list 方式操作 Rune 。 这种方法很容易引发崩溃, 具体原因取决于特定的语言,字符集和操作;

Symbol

一个 Symbol 对象表示 Dart 程序中声明的运算符或者标识符。

  • 你也许永远都不需要使用 Symbol ,但要按名称引用标识符的 API 时, Symbol 就非常有用了。
  • 因为代码压缩后会改变标识符的名称,但不会改变标识符的符号。
  • 通过字面量 Symbol ,也就是标识符前面添加一个 # 号,来获取标识符的 Symbol 。
  • Symbol 字面量是编译时常量
Symbol obj = new Symbol('name'); Symbol obj = #radix import 'dart:mirrors'; void main(){ 
    Symbol lib = new Symbol("foo_lib"); String name_of_lib = MirrorSystem.getName(lib); print(lib); print(name_of_lib); } // log: // Symbol("foo_lib")  // foo_lib 

函数——一等对象

Dart 是一门真正面向对象的语言, 甚至其中的函数也是对象,并且有它的类型 Function

// 虽然在 Effective Dart 中推荐 公共API中声明类型, 但是省略了类型声明,函数依旧是可以正常使用的 isNoble(atomicNumber) { 
    return _nobleGases[atomicNumber] != null; } // 箭头 语法:=> expr 语法是 { return expr; } 的简写 bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null; // 命名可选参数:使用 {param1, param2, …} 来指定命名参数 void enableFlags({ 
   bool bold, bool hidden}) { 
   ...} // 使用 @required 注释表示参数是 required 性质的命名参数 const Scrollbar({ 
   Key key, @required Widget child}) // 位置可选参数:将参数放到 [] 中来标记参数是可选的 String say(String from, String msg, [String device]) { 
   ...} // 默认参数值 void enableFlags({ 
   bool bold = false, bool hidden = false}) { 
   ...} // 为位置参数设置默认值 String say(String from, String msg, [String device = 'carrier pigeon', String mood]) { 
   ...} // list 或 map 可以作为默认值传递 void doStuff( { 
   List<int> list = const [1, 2, 3], Map<String, String> gifts = const { 
    'first': 'paper', 'second': 'cotton', 'third': 'leather' }}) { 
   ...} // main() 函数 // 任何应用都必须有一个顶级 main() 函数,作为应用服务的入口。 main() 函数返回值为空,参数为一个可选的 List<String>  void main(List<String> arguments){ 
   ...} // 匿名函数 // 有时候也被称为 lambda 或者 closure  ([[Type] param1[,]]) { 
    codeBlock; }; var list = ['apples', 'bananas', 'oranges']; list.forEach((item) { 
    print('${list.indexOf(item)}: $item'); }); // 如果函数只有一条语句, 可以使用箭头简写 list.forEach( (item) => print('${list.indexOf(item)}: $item')); 

测试函数是否相等:

void foo() { 
   } // 顶级函数 class A { 
    static void bar() { 
   } // 静态方法 void baz() { 
   } // 示例方法 } void main() { 
    var x; // 比较顶级函数。 x = foo; assert(foo == x); // 比较静态方法。 x = A.bar; assert(A.bar == x); // 比较实例方法。 var v = A(); // A的1号实例 var w = A(); // A的2号实例 var y = w; x = w.baz; // 两个闭包引用的同一实例(2号), // 所以它们相等。 assert(y.baz == x); // 两个闭包引用的非同一个实例, // 所以它们不相等。 assert(v.baz != w.baz); } 

词法作用域和词法闭包

词法作用域:

  • Dart 是一门词法作用域的编程语言,就意味着变量的作用域是固定的, 简单说变量的作用域在编写代码的时候就已经确定了。 花括号内的是变量可见的作用域。

词法闭包:

  • 闭包 即一个函数对象,即使函数对象的调用在它原始作用域之外, 依然能够访问在它词法作用域内的变量
// makeAdder() 捕获了变量 addBy /// 返回一个函数,返回的函数参数与 [addBy] 相加。 Function makeAdder(num addBy) { 
    return (num i) => addBy + i; } void main() { 
    // 创建一个加 2 的函数。 var add2 = makeAdder(2); // 创建一个加 4 的函数。 var add4 = makeAdder(4); assert(add2(3) == 5); assert(add4(3) == 7); } 

运算符

熟悉常用运算符即可:

  • 运算符
  • 算数运算符
  • 关系运算符
  • 类型判定运算符(参考关键字 asisis!
  • 赋值运算符
  • 复合赋值运算符((如 += )将算术运算符和赋值运算符组合在了一起)
    • =–=/=%=>>=^=+=*=~/=<<=&=|=
  • 逻辑运算符:!done && (col == 0 || col == 3)
  • 按位和移位运算符
  • 条件表达式
  • 级联运算符(..):.. 语法为 级联调用 (cascade)。 使用级联调用, 可以简化在一个对象上执行的多个操作。
  • 其他运算符

对于有两个操作数的运算符,运算符的功能由左边的操作数决定。 例如, 如果有两个操作数 Vector 和 Point, aVector + aPoint 使用的是 Vector 中定义的 + 运算符。

算数运算符使用举例:

assert(2 + 3 == 5); assert(2 - 3 == -1); assert(2 * 3 == 6); assert(5 / 2 == 2.5); // 结果是双浮点型 assert(5 ~/ 2 == 2); // 结果是整型 assert(5 % 2 == 1); // 余数 var a, b; a = 0; b = ++a; // a自加后赋值给b。 assert(a == b); // 1 == 1 a = 0; b = a++; // a先赋值给b后,a自加。 assert(a != b); // 1 != 0 a = 0; b = --a; // a自减后赋值给b。 assert(a == b); // -1 == -1 a = 0; b = a--; // a先赋值给b后,a自减。 assert(a != b); // -1 != 0 

关系运算符-判等:

  • 要测试两个对象x和y是否表示相同的事物, 使用 == 运算符
  • 在极少数情况下, 要确定两个对象是否完全相同,需要使用 identical() 函数
  • == 运算符的工作原理:
    • 如果 x 或 y 可以 null,都为 null 时返回 true ,其中一个为 null 时返回 false。
    • 结果为函数 x.==(y)的返回值,== 运算符执行的是第一个运算符的函数。 我们甚至可以重写很多运算符;

赋值运算符:

  • 使用 = 为变量赋值。 使用 ??= 运算符时,只有当被赋值的变量为 null 时才会赋值给它
// 将值赋值给变量a a = value; // 如果b为空时,将变量赋值给b,否则,b的值保持不变。 b ??= value; // 使用赋值和复合赋值运算符 var a = 2; // 使用 = 复制 a *= 3; // 复制并做乘法运算: a = a * 3 assert(a == 6); 

按位和移位运算符:

final value = 0x22; final bitmask = 0x0f; assert((value & bitmask) == 0x02); // AND assert((value & ~bitmask) == 0x20); // AND NOT assert((value | bitmask) == 0x2f); // OR assert((value ^ bitmask) == 0x2d); // XOR assert((value << 4) == 0x220); // Shift left assert((value >> 4) == 0x02); // Shift right 

条件表达式:

// 如果条件为 true, 执行 expr1 (并返回它的值): 否则, 执行并返回 expr2 的值 condition ? expr1 : expr2 // 如果 expr1 是 non-null, 返回 expr1 的值; 否则, 执行并返回 expr2 的值 expr1 ?? expr2 

级联运算符(..):

  • 级联运算符 (..) 可以实现对同一个对像进行一系列的操作
  • 除了调用函数, 还可以访问同一对象上的字段属性。 这通常可以节省创建临时变量的步骤, 同时编写出更流畅的代码。
querySelector('#confirm') // 获取对象 获取的对象依次执行级联运算符后面的代码 代码执行后的返回值会被忽略 ..text = 'Confirm' // 调用成员变量。 ..classes.add('important') ..onClick.listen((e) => window.alert('Confirmed!')); // 代码等价于 var button = querySelector('#confirm'); button.text = 'Confirm'; button.classes.add('important'); button.onClick.listen((e) => window.alert('Confirmed!')); // 级联运算符可以嵌套 final addressBook = (AddressBookBuilder() ..name = 'jenny' ..email = 'jenny@example.com' ..phone = (PhoneNumberBuilder() ..number = '415-555-0100' ..label = 'home') .build()) .build(); // 在返回对象的函数中谨慎使用级联操作符 var sb = StringBuffer(); sb.write('foo') // sb.write() 函数调用返回 void, 不能在 void 对象上创建级联操作 ..write('bar'); // Error: 'void' 没哟定义 'write' 函数。 

严格的来讲, “两个点” 的级联语法不是一个运算符。 它只是一个 Dart 的特殊语法。

其他运算符:

  • ():函数调用
  • []:获取List指定索引的值
  • .:对象成员获取,如foo.bar
  • ?.:带条件的对象成员获取,如foo?.bar
  • foo?.bar selects property bar from expression foo unless foo is null

控制流程语句

参考相关关键字:ifelseforwhiledo-whilebreakcontinueswitchcaseassert,使用try-catchthrow也可以改变程序流程;

异常

Dart 代码可以抛出和捕获异常。 异常表示一些未知的错误情况。 如果异常没有被捕获, 则异常会抛出, 导致抛出异常的代码终止执行:

  • 和 Java 有所不同, Dart 中的所有异常是非检查异常。 方法不会声明它们抛出的异常, 也不要求捕获任何异常。
  • Dart 提供了 ExceptionError 类型, 以及一些子类型;当然也可以定义自己的异常类型。
  • 此外 Dart 程序可以抛出任何非 null 对象, 不仅限 ExceptionError 对象。
  • 抛出异常是一个表达式, 所以可以在 => 语句中使用

参考相关关键字:trycatchrethrowonfinally

  • 使用 on 来指定异常类型, 使用 catch 来 捕获异常对象;
  • 不管是否抛出异常, finally 中的代码都会被执行;
  • 如果 catch 没有匹配到异常, 异常会在 finally 执行完成后,再次被抛出;
  • 任何匹配的 catch 执行完成后,再执行 finally
 try { 
    } on OutOfLlamasException { 
    // 指定具体异常 } on Exception catch (e) { 
    // 指定异常 } catch (e) { 
    // 没有指定的类型 }finally { 
    // Always clean up, even if an exception is thrown. } try { 
    // ··· } on Exception catch (e) { 
    } catch (e, s) { 
    print('Exception details:\n $e'); print('Stack trace:\n $s'); rethrow; } 

Dart 是一种基于类和 mixin 继承机制的面向对象的语言。 每个对象都是一个类的实例,所有的类都继承于 Object.

类的基本使用

 var p = Point(2, 2); p.y = 3; num distance = p.distanceTo(Point(4, 4)); p?.y = 4; var p1 = Point(2, 2); var p2 = Point.fromJson({ 
   'x': 1, 'y': 2}); // Dart 2 中 new是可选的 var p1 = new Point(2, 2); var p2 = new Point.fromJson({ 
   'x': 1, 'y': 2}); // 常量构造函数:在构造函数名之前加 const 关键字,来创建编译时常量时 var p = const ImmutablePoint(2, 2); var a = const ImmutablePoint(1, 1); var b = const ImmutablePoint(1, 1); assert(identical(a, b)); // 它们是同一个实例 // 这里有很多的 const 关键字。 const pointAndLine = const { 
    'point': const [const ImmutablePoint(0, 0)], 'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)], }; // 保留第一个 const 关键字,其余的全部省略:在 常量上下文 中, 构造函数或者字面量前的 const 可以省略 // 仅有一个 const ,由该 const 建立常量上下文。 const pointAndLine = { 
    'point': [ImmutablePoint(0, 0)], 'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)], }; 

获取对象的类型

使用对象的 runtimeType 属性, 可以在运行时获取对象的类型, runtimeType 属性回返回一个 Type 对象。

a.runtimeType print('The type of a is ${a.runtimeType}'); 

类的结构

class Point { 
    num x; // 声明示例变量 x,初始值为 null 。 num y; // 声明示例变量 y,初始值为 null 。 num z = 0; // 声明示例变量 z,初始值为 0 。 Point(num x, num y) { 
    // 还有更好的方式来实现下面代码。 this.x = x; this.y = y; } } class Point { 
    num x, y; // 在构造函数体执行前, // 语法糖已经设置了变量 x 和 y。 Point(this.x, this.y); } 

默认构造函数:

  • 在没有声明构造函数的情况下, Dart 会提供一个默认的构造函数。 默认构造函数没有参数并会调用父类的无参构造函数。
  • 子类不会继承父类的构造函数。 子类不声明构造函数,那么它就只有默认构造函数 (匿名,没有参数) ,构造函数不会被继承;

命名构造函数:

  • 使用命名构造函数可为一个类实现多个构造函数;
  • 构造函数不能够被继承, 这意味着父类的命名构造函数不会被子类继承。 如果希望使用父类中定义的命名构造函数创建子类, 就必须在子类中实现该构造函数;
class Point { 
    num x, y; Point(this.x, this.y); // 命名构造函数 Point.origin() { 
    x = 0; y = 0; } } 

调用父类非默认构造函数:

  • 默认情况下,子类的构造函数会自动调用父类的默认构造函数(匿名,无参数)。 父类的构造函数在子类构造函数体开始执行的位置被调用。 如果提供了一个 initializer list (初始化参数列表), 则初始化参数列表在父类构造函数执行之前执行。
    • initializer list (初始化参数列表
    • superclass’s no-arg constructor (父类的无名构造函数)
    • main class’s no-arg constructor (主类的无名构造函数)
  • 如果父类中没有匿名无参的构造函数, 则需要手工调用父类的其他构造函数。 在当前构造函数冒号 (:) 之后,函数体之前,声明调用父类构造函数。
    • 调用父类构造函数的参数无法访问 this
class Person { 
    String firstName; Person.fromJson(Map data) { 
    print('in Person'); } } class Employee extends Person { 
    // Person does not have a default constructor; // you must call super.fromJson(data). // super.fromJson(data)的参数data可以是一个表达式或者一个方法调用 Employee.fromJson(Map data) : super.fromJson(data) { 
    print('in Employee'); } } main() { 
    var emp = new Employee.fromJson({ 
   }); // Prints: // in Person // in Employee if (emp is Person) { 
    // Type check emp.firstName = 'Bob'; } (emp as Person).firstName = 'Bob'; } 

初始化列表:

  • 除了调用超类构造函数之外, 还可以在构造函数体执行之前初始化实例变量
  • 各参数的初始化用逗号分隔。
  • 在开发期间, 可以使用 assert 来验证输入的初始化列表。
  • 使用初始化列表可以很方便的设置 final 字段
// 在构造函数体执行之前, // 通过初始列表设置实例变量。 Point.fromJson(Map<String, num> json) : x = json['x'], y = json['y'] { 
    print('In Point.fromJson(): ($x, $y)'); } // 语法糖已经设置了变量 x 和 y。 Point.withAssert(this.x, this.y) : assert(x >= 0) { 
    print('In Point.withAssert(): ($x, $y)'); } // 使用初始化列表可以很方便的设置 final 字段 class Point { 
    final num x; final num y; final num distanceFromOrigin; Point(x, y) : x = x, y = y, distanceFromOrigin = sqrt(x * x + y * y); } main() { 
    var p = new Point(2, 3); print(p.distanceFromOrigin); } 

其他构造函数:

  • 重定向构造函数:有时构造函数的唯一目的是重定向到同一个类中的另一个构造函数。 重定向构造函数的函数体为空, 构造函数的调用在冒号 (:) 之后。
  • 常量构造函数:如果该类生成的对象是固定不变的, 那么就可以把这些对象定义为编译时常量。 为此,需要定义一个 const 构造函数, 并且声明所有实例变量为 final
  • 工厂构造函数:当执行构造函数并不总是创建这个类的一个新实例时,则使用 factory 关键字(工厂构造函数无法访问 this);
// 重定向构造函数 class Point { 
    num x, y; // 类的主构造函数。 Point(this.x, this.y); // 指向主构造函数 Point.alongXAxis(num x) : this(x, 0); } // 常量构造函数 class ImmutablePoint { 
    static final ImmutablePoint origin = const ImmutablePoint(0, 0); final num x, y; const ImmutablePoint(this.x, this.y); } // 工厂构造函数 class Logger { 
    final String name; bool mute = false; // 从命名的 _ 可以知, // _cache 是私有属性。 static final Map<String, Logger> _cache = <String, Logger>{ 
   }; factory Logger(String name) { 
    if (_cache.containsKey(name)) { 
    return _cache[name]; } else { 
    final logger = Logger._internal(name); _cache[name] = logger; return logger; } } Logger._internal(this.name); void log(String msg) { 
    if (!mute) print(msg); } } 

抽象类:

  • 使用 abstract 修饰符来定义 抽象类 — 抽象类不能实例化。 抽象类通常用来定义接口,以及部分实现。 如果希望抽象类能够被实例化,那么可以通过定义一个 工厂构造函数 来实现。

方法

  • 对象的实例方法可以访问 this 和实例变量
  • Getter 和 Setter,使用 get 和 set 关键字实现 Getter 和 Setter ,能够为实例创建额外的属性。
  • 最开始实现 Getter 和 Setter 也许是直接返回成员变量; 随着需求变化, Getter 和 Setter 可能需要进行计算处理而使用方法来实现;
  • 抽象方法:只定义接口不进行实现,而是留给其他类去实现。 抽象方法只存在于 抽象类 中
  • 调用抽象方法会导致运行时错误
abstract class Doer { 
    // 定义实例变量和方法 ... void doSomething(); // 定义一个抽象方法。 } class EffectiveDoer extends Doer { 
    void doSomething() { 
    // 提供方法实现,所以这里的方法就不是抽象方法了... } } 

隐式接口:

  • 每个类都隐式的定义了一个接口,接口包含了该类所有的实例成员及其实现的接口。 如果要创建一个 A 类,A 要支持 B 类的 API ,但是不需要继承 B 的实现, 那么可以通过 A 实现 B 的接口。
  • 一个类可以通过 implements 关键字来实现一个或者多个接口, 并实现每个接口要求的 API。如class Point implements Comparable, Location {...}
// person 类。 隐式接口里面包含了 greet() 方法声明。 class Person { 
    // 包含在接口里,但只在当前库中可见。 final _name; // 不包含在接口里,因为这是一个构造函数。 Person(this._name); // 包含在接口里。 String greet(String who) => 'Hello, $who. I am $_name.'; } // person 接口的实现。 class Impostor implements Person { 
    get _name => ''; String greet(String who) => 'Hi $who. Do you know who I am?'; } String greetBob(Person person) => person.greet('Bob'); void main() { 
    print(greetBob(Person('Kathy'))); print(greetBob(Impostor())); } 

扩展类——继承:

  • 使用 extends 关键字来创建子类, 使用 super 关键字来引用父类
class Television { 
    void turnOn() { 
    _illuminateDisplay(); _activateIrSensor(); } // ··· } class SmartTelevision extends Television { 
    void turnOn() { 
    super.turnOn(); _bootNetworkInterface(); _initializeMemory(); _upgradeApps(); } // ··· } 

重写类成员:

  • 子类可以重写实例方法,getter 和 setter。 可以使用 @override 注解指出想要重写的成员
class SmartTelevision extends Television { 
    @override void turnOn() { 
   ...} // ··· } 

重写运算符:参考关键字 operator

noSuchMethod()

  • 当代码尝试使用不存在的方法或实例变量时, 通过重写 noSuchMethod() 方法,来实现检测和应对处理
class A { 
    // 如果不重写 noSuchMethod,访问 // 不存在的实例变量时会导致 NoSuchMethodError 错误。 @override void noSuchMethod(Invocation invocation) { 
    print('You tried to use a non-existent member: ' + '${invocation.memberName}'); } } 

类变量和方法

参考相关关键字:static

枚举

参考相关关键字:enum

为类添加功能

参考相关关键字:mixin

泛型

在 API 文档中你会发现基础数组类型 List 的实际类型是List<E><…> 符号将 List 标记为 泛型 (或 参数化) 类型。 这种类型具有形式化的参数。 通常情况下,使用一个字母来代表类型参数, 例如 E, T, S, K, 和 V 等

  • 正确指定泛型类型可以提高代码质量。
  • 使用泛型可以减少重复的代码。
// 一个用于缓存对象的接口 abstract class ObjectCache { 
    Object getByKey(String key); void setByKey(String key, Object value); } // 一个相同功能的字符串类型接口 abstract class StringCache { 
    String getByKey(String key); void setByKey(String key, String value); } // 泛型可以省去创建所有这些接口的麻烦 abstract class Cache<T> { 
    T getByKey(String key); void setByKey(String key, T value); } 

使用集合字面量:

  • List , Set 和 Map 字面量也是可以参数化的。 参数化字面量和之前的字面量定义类似, 对于 List 或 Set 只需要在声明语句前加 <type> 前缀, 对于 Map 只需要在声明语句前加 <keyType, valueType> 前缀
  • var names = <String>['Seth', 'Kathy', 'Lars'];
  • var uniqueNames = <String>{'Seth', 'Kathy', 'Lars'};
  • var pages = <String, String>{};

使用泛型类型的构造函数:

  • 在类名字后面使用尖括号(<...>)来指定泛型类型;
    • Set<String>.from(names);
    • Map<int, View>();

运行时中的泛型集合:

  • Dart 中泛型类型是 固化的,也就是说它们在运行时是携带着类型信息的。 例如, 在运行时检测集合的类型(Java中的泛型会被 擦除);
var names = List<String>(); names.addAll(['Seth', 'Kathy', 'Lars']); print(names is List<String>); // true 

限制泛型类型:

  • 可以使用 extends 实现参数类型的限制
    • class Foo<T extends SomeBaseClass> {...},如果Foo()时不指定泛型,那么T就是SomeBaseClass

使用泛型函数:

  • T first<T>(List<T> ts) {};

库和可见性

参考相关关键字:importlibraryasshowhidedeferred as

实现库

实现库、包:

  • 如何组织库的源文件。
  • 如何使用 export 命令。
  • 何时使用 part 命令。
  • 何时使用 library 命令。

异步支持

Dart 库中包含许多返回 Future 或 Stream 对象的函数. 这些函数在设置完耗时任务(例如 I/O 曹组)后, 就立即返回了,不会等待耗任务完成。 使用 async 和 await 关键字实现异步编程;

  • Future处理
  • 声明异步函数
  • 处理Stream
  • 生成器:参考相关关键字syncyield

Future处理:

  • 使用 asyncawait;(使用 trycatch, 和 finally 来处理代码中使用 await 导致的错误)
  • 使用 Future API;

声明异步函数:

  • 函数体被 async 标示符标记的函数,即是一个_异步函数_;
  • async 关键字添加到函数使其返回Future;
  • Future<String> lookUpVersion() async => '1.0.0';

注意,函数体不需要使用Future API。 如有必要, Dart 会创建 Future 对象。如果函数没有返回有效值, 需要设置其返回类型为 Future<void>

处理Stream:

  • 从 Stream 中获取数据值的两种方式;
  • 使用 async 和 一个 异步循环 (await for
  • 使用 Stream API;
Future main() async { 
    // ... await for (var request in requestServer) { 
    handleRequest(request); } // ... } 

上面 表达式 返回的值必须是 Stream 类型。 执行流程如下:

  • 等待,直到流发出一个值。
  • 执行 for 循环体,将变量设置为该发出的值
  • 重复1和2,直到关闭流。
  • 使用 break 或者 return 语句可以停止接收 stream 的数据, 这样就跳出了 for 循环, 并且从 stream 上取消注册

提示: 在使用 await for 前,确保代码清晰, 并且确实希望等待所有流的结果。 例如,通常不应该使用 await for 的UI事件侦听器, 因为UI框架会发送无穷无尽的事件流。

可调用类

通过实现类的 call() 方法, 能够让类像函数一样被调用

class WannabeFunction { 
    call(String a, String b, String c) => '$a $b $c!'; } main() { 
    var wf = new WannabeFunction(); var out = wf("Hi","there,","gang"); print('$out'); } 

Isolates

大多数计算机中,甚至在移动平台上,都在使用多核CPU。 为了有效利用多核性能,开发者一般使用共享内存数据来保证多线程的正确执行。 然而, 多线程共享数据通常会导致很多潜在的问题,并导致代码运行出错。

所有 Dart 代码都在隔离区( isolates )内运行,而不是线程。 每个隔离区都有自己的内存堆,确保每个隔离区的状态都不会被其他隔离区访问

元数据

使用元数据可以提供有关代码的其他信息。 元数据注释以字符 @ 开头, 后跟对编译时常量 (如 deprecated) 的引用或对常量构造函数的调用

对于所有 Dart 代码有两种可用注解:

  • @deprecated
  • @override

可以自定义元数据注解

元数据可以在 library、 class、 typedef、 type parameter、 constructor、 factory、 function、 field、 parameter 或者 variable 声明之前使用,也可以在 import 或者 export 指令之前使用。

使用反射可以在运行时获取元数据信息

注释

  • 单行注释
  • 多上注释
  • 文档注释

文档注释以 /// 或者 / 开始。 在连续行上使用 /// 与多行文档注释具有相同的效果

/// A domesticated South American camelid (Lama glama). /// /// 自从西班牙时代以来, /// 安第斯文化就将骆驼当做肉食类和运输类动物。 class Llama { 
    String name; /// 喂养骆驼 [Food]. /// /// 典型的美洲驼每周吃一捆干草。 void feed(Food food) { 
    // ... } /// 使用 [activity] 训练骆驼 /// [timeLimit] 分钟。 void exercise(Activity activity, int timeLimit) { 
    // ... } } 

解析 Dart 代码并生成 HTML 文档,可以使用 SDK 中的 documentation generation tool.

总结

不清晰的地方:

  • 库、包的开发;
  • 元数据编程;
  • 异步Stream编程;
  • 文档生成;
  • 隔离区isolates的使用;
  • 生成器

Dart语言概述 over!

到此这篇dart编程语言pdf下载_fortran编程语言的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • dart语言怎么样_主流编程语言2024-11-14 13:45:08
  • dart编程语言pdf_第一个编程语言2024-11-14 13:45:08
  • dart语言怎么样_做游戏用什么编程语言2024-11-14 13:45:08
  • 探秘《Dart编程语言实战》:深入理解与高效开发2024-11-14 13:45:08
  • dart编程语言pdf下载_C语言基础2024-11-14 13:45:08
  • dart编程语言pdf_scratch2.0编程2024-11-14 13:45:08
  • dart编程语言pdf下载_哪种编程语言最难学2024-11-14 13:45:08
  • dart语言 知乎_fortran编程语言2024-11-14 13:45:08
  • 编程语言讲解_古老的编程语言2024-11-14 13:45:08
  • 编程语言的“别样”编年史2024-11-14 13:45:08
  • 全屏图片