Dart学习笔记

Dart语法预览

重要概念:

  1. 所有能够使用变量引用的都是对象, 每个对象都是一个的实例。在 Dart 中 甚至连 数字、方法和 null 都是对象。所有的对象都继承于 Object 类。
  2. 指定的类型为静态类型,没有指定类型的变量的类型为 dynamic。
  3. Dart 在运行之前会先解析你的代码。你可以通过使用 类型或者编译时常量来帮助 Dart 去捕获异常以及 让代码运行的更高效。
  4. Dart 没有 public、 protected、 和 private 关键字。如果一个标识符以 (_) 开头,则该标识符 在库内是私有的。
  5. 能够使用变量引用的都是对象,包括数字、方法、null等,默认值都为null
  6. final和const:const是编译时常量,也是final。实例变量使用final,使用const则需要添加static:static const。const修饰的构造函数创建的对象所有属性不可变。
// 类外面
const nameOut = "name_out"; // 不可变
final nameOut2 = "name_out2"; // 必须进行初始化,不可变
// static const nameOut3 = "name_out3"; 错误用法,static不能用在类外面

class TestClass {
  /*
* static 标识一个成员属于类而不是对象
* final 必须初始化,初始化后值不可变,编译时不能确定值
* const 编译时可确定,并且不能修改
* */

  final name; // 需要在构造方法里初始化
  static const name2 = 'name2'; // 只有静态变量才能被定义为const
  final name3 = 'name3'; // 定义时初始化
  
  TestClass(this.name);

  void test() {
    const name4 = "name4"; // 不可变
    var name5 = "name5"; // 可变
    final name6 = "name6"; // 不可变
//    static 不可用在方法里
//    static final name7 = "name7";

//    name4 = "name"; // 不可更改
    name5 = "name";
//    name6 = "name"; // 不可更改

    print("测试:$name4, $name5, $name6");
  }
}

知识点:

  • 数值numbers : num/int/double
字符串与数字之间的转换:
var one = int.parse('1');
var onePointOne = double.parse('1.1');
String oneAsString = 1.toString();
String piAsString = 3.14159.toStringAsFixed(2);
  • 字符串Strings(UTF-16编码)
  1. 使用单引号或者双引号来创建。Dart 字符串是 UTF-16 编码的字符序列。
  2. 插值操作:$变量${表达式},表达式的结果为一个对象,则 Dart 会调用对象的 toString() 函数来获取一个字符串。
  3. == 操作符判断两个对象的内容是否一样。
  4. 使用三个单引号或者双引号也可以 创建多行字符串对象。
  5. 使用r'字符串内容'创建原始值字符串,其中的\n也是字符串内容,而不是换行。
  6. 字符串编译时常量可以插入常量,不能插入变量。
  • 布尔值Booleans
    类型bool,包含true/false。true为true,其他所有值均为false。
  • 列表Lists
    类型List,使用[],可使用const来定义一个不可变的List(编译时常量)。
  • 键值对象Maps
    类型Map,使用{},使用 .length 来获取 map 中键值对的数目,可使用const来定义一个不可变的Map(编译时常量)。<String, Logger>{}
  • Runes
    字符串的UTF-32编码值。
    使用codeUnitAt 和 codeUnit 属性返回 16-bit code units。
    使用runes返回32-bit Unicode。
var clapping = '\u{1f44f}';
  print(clapping); // 👏
  print(clapping.codeUnitAt(0)); // 55357
  print(clapping.codeUnits); // [55357, 56399]
  print(clapping.runes.toList()); // (128079)、 toList(): [128079]

  Runes input = new Runes(
      '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
  print(new String.fromCharCodes(input)); // ♥  😅  😎  👻  🖖  👍
  • Symbols
    一个 Symbol object 代表 Dart 程序中声明的操作符或者标识符。
    使用 Symbol 字面量来获取标识符的 symbol 对象,也就是在标识符 前面添加一个 # 符号。#radix
  • Functions方法
  1. 只有一个表达式,使用=>语法。
  2. 参数分必须参数和可选参数,可选参数分为可选命名参数和可选位置参数。
  3. 可选命名参数
定义:
enableFlags({bool bold, bool hidden}) {
}
使用:
enableFlags(bold: true, hidden: false);
  1. 可选位置参数
定义,参数放在[]中:
String say(String from, String msg, [String device]) {
}
使用:
say('Bob', 'Howdy') // 未使用位置参数
say('Bob', 'Howdy', 'smoke signal') // 使用了位置参数
  1. 可选参数的默认参数值
    默认值只能用编译时常量,否则为null。list和map也可以作为默认参数。
void enableFlags({bool bold = false, bool hidden = false}) {
}
  1. main函数
    程序的入口函数。
  2. 一等方法
    可以把其他方法作为参数调用的方法。
class Test {
  printEelement(element) {
    print(element);
  }

  void test() {
    var list = [1, 2, 3];
    list.forEach(printEelement);
  }
}

var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
  1. 匿名方法
    也叫lambda或者closure闭包。
var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums'];
    list.forEach((i) {
      print(list.indexOf(i).toString() + ': ' + i);
    });

list.forEach((i) => print(list.indexOf(i).toString() + ': ' + i));
  1. 函数返回值
    如果函数没有指定返回值,则最后一句默认执行return null;
  • 操作符
  1. ~/:返回值为整数的除法
  2. as 类型转换,is 是否为某个类型 is!是否不是某个类型
  3. ??= 赋值操作符,如果为null,则赋值,否则不赋值
  4. 条件表达式:
    条件 ? 表达式1 : 表达式2 ,用于赋值操作
    表达式1 ?? 表达式2 ,用于判断是否为null操作
  5. 级联操作符..
querySelector('#button') // Get an object.
  ..text = 'Confirm'   // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));
第一个方法 querySelector() 返回了一个 selector 对象。 后面的级联操作符都是调用这个对象的成员, 并忽略每个操作 所返回的值。

无法再返回值为void上执行级联操作

  1. 其他操作符 ?.,类似swift的可选类型的可选调用,左侧为null,则返回null。
  • 流程控制
  1. 实现了Iterable接口的对象,都可以使用forEach来遍历操作,List和Map都实现了Iterable接口。
candidates元素对象实现了Iterable接口:
candidates.where((c) => c.yearsExperience >= 5)
          .forEach((c) => c.interview());
先筛选出大于等于5的元素,再遍历每个元素进行操作。
  1. for in 循环,支持实现了Iterable接口的对象
var collection = [0, 1, 2];
for (var x in collection) {
  print(x);
}
  1. switch语句
    每个case必须有break,空的case语句不用写break,class中必须没有覆写==。
    支持标签,continue支持跳转到某个标签。
var command = 'CLOSED';
switch (command) {
  case 'CLOSED':
    executeClosed();
    continue nowClosed;
    // Continues executing at the nowClosed label.

nowClosed:
  case 'NOW_CLOSED':
    // Runs for both CLOSED and NOW_CLOSED.
    executeNowClosed();
    break;
}
  1. Assert断言
    assert只在检查模式生效,生产模式不执行。
  • 异常
  1. 使用throw抛出异常,也可以抛出任何对象。
  2. throw是一个表达式,可以用在表达式的场景。
try {
      // 异常
    } on Exception {
      // A specific exception
      print("");
    } on Exception catch (e) {
      // Anything else that is an exception
      print('Unknown exception: $e');
    } catch (e) {
      // No specified type, handles all
      print('Something really unknown: $e');
    }
  1. 使用 rethrow 关键字可以 把捕获的异常给 重新抛出。
  2. finally确保不管是否异常都执行的语句。
  • 类Classes
  1. 单继承
  2. 使用new 和构造函数来创建对象,如果为常量构造函数,则使用const替代new。
  3. 使用runtimeType判断实例的类型,返回一个Type类型。
  4. 每个实例变量为自动生成一个getter。每个none-final变量会自动生成一个setter。
  5. 构造函数中,当名字冲突的时候才使用this,否则忽略this。
构造函数的简化:
Point(num x, num y) {
    this.x = x;
    this.y = y;
  }
简化为:
Point(this.x, this.y);
  1. 如果你没有定义构造函数,则会有个默认构造函数。 默认构造函数没有参数,并且会调用超类的 没有参数的构造函数。
  2. 子类不会继承超类的构造函数。 子类如果没有定义构造函数,则只有一个默认构造函数 (没有名字没有参数)。
  3. 使用命名构造函数可以为一个类实现多个构造函数。
class Test {
  num x;
  num y;

  // 构造函数简化
  Test(this.x, this.y);

  // 命名构造函数
  Test.fromJson(Map json) {
    x = json['x'];
    y = json['y'];
  }
}
  1. 超类的构造函数调用顺序
    子类提供了有参的构造函数,顺序为:
    初始化子类的参数列表 => 调用父类的无名构造函数 => 调用子类的无名构造函数
    如果超类没有无名构造函数,则可以在构造函数后使用:来调用,如下:
  2. 初始化参数列表
    在构造函数体执行之前除了可以调用超类构造函数之外,还可以 初始化实例参数。 使用逗号分隔初始化表达式。
class Point {
  num x;
  num y;

  Point(this.x, this.y);

  // Initializer list sets instance variables before
  // the constructor body runs.
  Point.fromJson(Map jsonMap) : x = jsonMap['x'], y = jsonMap['y'] {
    print('In Point.fromJson(): ($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);
}
  1. 重定向构造函数
    构造函数需要调用其他构造函数,可以在后遭函数定义后,使用冒号调用其他构造函数。
import 'dart:math';

main() {
  var p = new Point(2, 3);
}

class Point {
  final num x;
  final num y;

  Point(this.x, this.y);
  // 重定向构造函数
  Point.fromXAlixs(num x) : this(x, 0);
}
  1. 常量构造函数
    使用const修饰构造函数,其他变量均使用final来修饰。
  2. 工厂构造函数
    使用factory来修饰的构造函数,工厂构造函数无法使用this。
    工厂方法用于写单例:
// 用户单例

class UserManager {
  // 如果一个函数的构造方法并不总是返回一个新的对象的时候,可以使用factory,
  // 比如从缓存中获取一个实例并返回或者返回一个子类型的实例

  // 工厂方法构造函数
  factory UserManager() => _getInstance();

  // instance的getter方法,通过UserManager.instance获取对象
  static UserManager get instance => _getInstance();

  // 静态变量_instance,存储唯一对象
  static UserManager _instance;

  // 私有的命名式构造方法,通过它可以实现一个类可以有多个构造函数,
  // 子类不能继承internal不是关键字,可定义其他名字
  UserManager._internal() {
    // 初始化
    user = new User(false, "", "", "", "", false, "", false, "", "");
  }

  // 获取对象
  static UserManager _getInstance() {
    if (_instance == null) {
      // 使用私有的构造方法来创建对象
      _instance = new UserManager._internal();
    }
    return _instance;
  }

  // 用户对象
  User user;
}
  • getters/setters
    每个实例变量都有一个getter,非final的变量有一个setter,可以自定义getter和setter。
main() {
  var rect = Rectangle(3, 9, 5, 7);
  print(rect.left);
  rect.right = 10;
  print(rect.left);
  rect.left = 1;
  print(rect.right);
}

class Rectangle {
  num left;
  num top;
  num width;
  num 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;
  num set(num value) => top = value - height;
}
  • 抽象函数
    抽象函数是只定义函数接口但是没有实现的函数,由子类来 实现该函数。如果用分号来替代函数体则这个函数就是抽象函数。
    定义了函数,没有函数的实现,这个函数就是抽象函数。
  • 覆写操作符
    使用operator修饰
main() {
  var v1 = Vector(2, 5);
  var v2 = Vector(3, 4);
  print((v1 + v2).x);
  print((v1 + v2).y);
}

class Vector {
  final int x;
  final int y;
  const Vector(this.x, this.y);

  Vector operator +(Vector v) {
    return new Vector(x + v.x, y + v.y);
  }

  Vector operator -(Vector v) {
    return new Vector(x - v.x, y - v.y);
  }
}
  • 抽象类
    使用abstract修饰class,可以定义抽象函数和非抽象函数(有函数实现)不能被实例化。
main() {
  var sub = SubAbstractClass();
  sub.test();
  sub.test2();
}

abstract class AbstractClass {
  void test();

  void test2() {
    print('test2');
  }
}

class SubAbstractClass extends AbstractClass {
  @override
  void test() {
    // TODO: implement test
    print('test1');
  }
}
  • 隐式接口
    每个类都隐式的定义了一个包含所有实例成员的接口, 并且这个类实现了这个接口。如果你想 创建类 A 来支持 类 B 的 api,而不想继承 B 的实现, 则类 A 应该实现 B 的接口。
class Person {
  final _name;
  Person(this._name);

  void look() {
    print('look');
  }
}

class Student implements Person {
  @override
  get _name => "";

  @override
  void look() {
    // TODO: implement look
  }
}

每个类都有一个隐式的接口,供别的类来implements实现。

  • 扩展子类
    使用extends来创建子类。
  • 枚举
    enum,枚举无法继承,无法使用mixin,无法implements枚举类型。
  • mixin为类添加新功能
    mixins在多类继承中重用一个类的代码。使用with关键字。

mixin类的条件:

  1. 继承于Object而不是继承与其他类
  2. 没有构造函数
  3. 不能调用super
main() {
  var stu1 = Student('ying');
  stu1.look();
  stu1.buyBook();
  stu1.readBook();
}

class Person {
  final _name;
  Person(this._name);

  void look() {
    print('look ' + this._name);
  }
}

// mixin类
class BookHandle {
  void readBook() {
    print('read book');
  }

  void buyBook() {
    print('buy book');
  }
}

class Student extends Person with BookHandle {
  Student(name) : super(name);
}
  • implements/extends/mixin区别:
    implements实现多个别的类的接口,不通过继承来实现,每一个类都有一个包含所有实例成员的隐式接口。
    extends 创建子类,只能单继承
    mixin用于多类集成中重用一个类的代码,解决代码复用问题,不用再去实现同样的功能,用with关键字
    mixin类条件:1.继承于Object而不是其他类(不再限定) 2. 没有构造函数 3. 不能调用super(不再限定)
// 普通类
class AClass {
  void test() {

  }
}

// 抽象类
abstract class BClass {
  void testB();
}

// 抽象类
abstract class CClass {
  void testC();
}

// 使用implements可以实现多个类的接口
class DClass implements BClass, CClass {
  @override
  void testB() {
    // TODO: implement testB
  }

  @override
  void testC() {
    // TODO: implement testC
  }
}

// 使用extends单继承
class EClass extends AClass {

}

// 创建mixin
mixin FClass {
  final String name = "";

  // 需要被实现
  void testF();

  // mixin实现的功能
  void testFF() {
    print('testFF');
  }
}

// 也是一个mixin,不再限定必须是继承于Object和不再限定不能调用super
abstract class GClass {
  void testG();

  void testGG() {
    print('testGG');
  }
}

// 使用minxin
class HClass with FClass, GClass {
  @override
  void testF() {
    // TODO: implement testF
    testFF();
  }
  
  @override
  void testG() {
    // TODO: implement testG
    testGG();
  }
}
  • 静态变量和静态函数
  1. 使用static修饰,静态变量第一次使用的时候才初始化。无法使用this访问。
  2. 对于通用的或者经常使用的静态函数,考虑 使用顶级方法而不是静态函数。
  3. 静态函数还可以当做编译时常量使用。例如, 你可以把静态函数当做常量构造函数的参数来使用。
  • 泛型
  1. 用于集合字面量
var names = <String>['ni', 'wo', 'ta'];
var pages = <String, String>{'name': 'tom'};
  1. 构造函数中使用泛型
var list = new List<String>();
var nameSet = new Set<String>.from(list);
  1. 用于is判断类型
    nameSet is Set<String>
  2. 限制泛型类型
    class Foo<T extends SomeBaseClass> {...}
  3. 泛型函数
main() {
  var test = new Test();
  var names = new List<String>();
  test.firstTest(names);
}

class Test {
  T firstTest<T>(List<T> list) {
    if (list.length > 0) {
      T tmp = list[0];
      return tmp;
    } else {
      return null;
    }
  }
}
  • 库和可见性
  1. 一个Dart app就是一个库,使用下划线的标识符只在库里可见。
  2. 使用库
    内置库:import 'dart:io';
    包管理器提供的库:import 'package:utils/utils.dart';
    其他库:文件系统路径
  3. 使用as 指定库的前缀,来区分不同的标识符:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// ...
Element element1 = new Element();           // Uses Element from lib1.
lib2.Element element2 = new lib2.Element(); // Uses Element from lib2.
  1. 导入库的一部分
    使用show/hide关键字:
// 只导入foo
import 'package:lib1/lib1.dart' show foo;

// 导入除foo之外的其他
import 'package:lib2/lib2.dart' hide foo;
  1. 延迟载入库
    可以让应用在需要的时候再 加载库,使用场景:
    减少app的启动时间执行不同的测试加载很少使用的功能
    使用方法:
    先用deferred as来导入,用的时候,调用loadLibrary()函数来加载库。
import 'package:deferred/hello.dart' deferred as hello;

greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}

loadLibrary()多次调用只加载一次,async异步加载库,await关键字暂停代码执行一直到库加载完成。

延迟加载库的常量在导入的时候是不可用的。 只有当库加载完毕的时候,库中常量才可以使用。
在导入文件的时候无法使用延迟库中的类型。 如果你需要使用类型,则考虑把接口类型移动到另外一个库中, 让两个库都分别导入这个接口库。
Dart 隐含的把 loadLibrary() 函数导入到使用 deferred as 的命名空间 中。 loadLibrary() 方法返回一个 Future。

  • 异步支持
    一些异步返回Future和Stream对象的方法,设置完就返回了,无需等待操作完成。
  1. 使用Future对象中的数据
    使用async和await使用Future API
  2. 从Stream中获取数据
    使用async和一个异步for循环(await for)Stream API

要使用await,方法必须带有async

checkVersion() async {
  var version = await lookUpVersion();
  if (version == expectedVersion) {
    // Do something.
  } else {
    // Do something else.
  }
}
  1. 异步方法
    使用async标记函数体的方法,异步方法返回值为Future。
同步方法:
String lookUpVersionSync() => '1.0.0';
异步方法:
Future<String> lookUpVersion() async => '1.0.0';
  1. await表达式
    一个异步方法可以有多个await表达式,默认返回一个Future,如果表达式返回值不是Future,会自动把返回值放到Future中返回。
  2. await for
    await for是作用域Stream的异步for循环。expression表达式的返回值必须是Stream类型的。可使用break和return来控制流程。
await for (variable declaration in expression) {
  // Executes each time the stream emits a value.
}
  • 可调用的类
    如果类实现了call方法,则可以像调用方法一样调用类。
main() {
  var test = new Test();
  var out = test('name', 1, true);
  print(out); // name, 1, true
}

class Test {
  call(String str, int num, bool bo) => str + ',' + ' $num, $bo';
}
  • typedef类型定义
    typedef只适用于Function方法类型。
    typedef int Compare(Object a, Object b);

  • 注解
    使用元数据给你的代码添加其他额外信息。
    元数据注解是以 @ 字符开头,后面是一个编译时 常量(例如 deprecated)或者 调用一个常量构造函数。
    3个通用的注解:@deprecated不赞成、 @override重写、 和 @proxy代理。
    自定义注解:
    todo.dart文件:

library todo;

class todo {
  final String who;
  final String what;
  
  const todo(this.who, this.what);
}

main.dart文件:

import 'todo.dart';

main() {

}

class Test {
  @todo('ying', 'to do something')
  void test() {
    print('test');
  }
}
  • 注释
    ///* */////** */
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342