Dart--学习

参考的网站:

Dart 语法预览

Dart 的在线编辑器,DartPad 。

视频:

Flutter开发第一步-Dart编程语言入门

 

以下的内容是参考:Dart 语法预览

Dart 的语法有几个比较特别的,先记录下来:

(1)所有能够使用变量引用的都是对象, 每个对象都是一个类的实例。在 Dart 中 甚至连 数字、方法和 null 都是对象。所有的对象都继承于 Object 类。

(2)Final and const
如果你以后不打算修改一个变量,使用 final 或者 const。 一个 final 变量只能赋值一次;一个 const 变量是编译时常量。 (Const 变量同时也是 final 变量。) 顶级的 final 变量或者类中的 final 变量在 第一次使用的时候初始化。

(3)Built-in types(内置的类型)
Dart 内置支持下面这些类型:
numbers,int 和 double 都是 num 的子类。
strings,注意: == 操作符判断两个对象的内容是否一样。 如果两个字符串包含一样的字符编码序列, 则他们是相等的。
booleans
lists (也被称之为 arrays)
maps
runes (用于在字符串中表示 Unicode 字符),不常用;
symbols,也是不常用;

 

使用三个引号或双引号创建多行字符串;使用r创建原始raw字符串,见下图;

 

(4)Functions(方法)
Dart 是一个真正的面向对象语言,方法也是对象并且具有一种 类型, Function。 这意味着,方法可以赋值给变量,也可以当做其他方法的参数。 也可以把 Dart 类的实例当做方法来调用。

方法的参数,有可选命名参数{},可选位置参数[],默认参数值。

(5)Cascade notation (..)(级联操作符)
级联操作符 (..) 可以在同一个对象上 连续调用多个函数以及访问成员变量。 使用级联操作符可以避免创建 临时变量, 并且写出来的代码看起来 更加流畅:

(6)Anonymous functions(匿名方法)
大部分方法都带有名字,例如 main() 或者 printElement()。 你有可以创建没有名字的方法,称之为 匿名方法,有时候也被称为 lambda 或者 closure 闭包。 你可以把匿名方法赋值给一个变量, 然后你可以使用这个方法,比如添加到集合或者从集合中删除。
匿名函数和命名函数看起来类似— 在括号之间可以定义一些参数,参数使用逗号 分割,也可以是可选参数。 后面大括号中的代码为函数体:
([[Type] param1[, …]]) { 
  codeBlock; 
}; 

(7)Lexical closures(词法闭包)
一个 闭包 是一个方法对象,不管该对象在何处被调用, 该对象都可以访问其作用域内 的变量。
方法可以封闭定义到其作用域内的变量。 下面的示例中,makeAdder() 捕获到了变量 addBy。 不管你在那里执行 makeAdder() 所返回的函数,都可以使用 addBy 参数。

/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(num addBy) {
  return (num i) => addBy + i;
}

main() {
  // Create a function that adds 2.
  var add2 = makeAdder(2);

  // Create a function that adds 4.
  var add4 = makeAdder(4);

  assert(add2(3) == 5);
  assert(add4(3) == 7);
}

(8)Type test operators(类型判定操作符)
as、 is、 和 is! 操作符是在运行时判定对象 类型的操作符:

操作符解释
as类型转换
is如果对象是指定的类型返回 True
is!如果对象是指定的类型返回 False

只有当 obj 实现了 T 的接口, obj is T 才是 true。例如 obj is Object 总是 true。

(9)Cascade notation (..)(级联操作符)
级联操作符 (..) 可以在同一个对象上 连续调用多个函数以及访问成员变量。 使用级联操作符可以避免创建 临时变量, 并且写出来的代码看起来 更加流畅:
例如下面的代码:

querySelector('#button') // Get an object.
  ..text = 'Confirm'   // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));

第一个方法 querySelector() 返回了一个 selector 对象。 后面的级联操作符都是调用这个对象的成员, 并忽略每个操作 所返回的值。
上面的代码和下面的代码功能一样:

var button = querySelector('#button');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));

注意: 严格来说, 两个点的级联语法不是一个操作符。 只是一个 Dart 特殊语法。
 

(10)Exceptions(异常)

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // A specific exception
  buyMoreLlamas();
} 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');
}

如上面的代码:你可以使用on 或者 catch 来声明捕获语句,也可以 同时使用。使用 on 来指定异常类型,使用 catch 来 捕获异常对象。

//  函数 catch() 可以带有一个或者两个参数, 第一个参数为抛出的异常对象, 第二个为堆栈信息 (一个 StackTrace 对象)。

} on Exception catch (e) {
  print('Exception details:\n $e');
} catch (e, s) {
  print('Exception details:\n $e');
  print('Stack trace:\n $s');
}
// 使用 rethrow 关键字可以 把捕获的异常给 重新抛出。

final foo = '';

void misbehave() {
  try {
    foo = "You can't change a final variable's value.";
  } catch (e) {
    print('misbehave() partially handled ${e.runtimeType}.');
    rethrow; // Allow callers to see the exception.
  }
}

void main() {
  try {
    misbehave();
  } catch (e) {
    print('main() finished handling ${e.runtimeType}.');
  }
}

(11)常量构造函数
使用常量构造函数 可以创建编译时常量,要使用常量构造函数只需要用 const 替代 new 即可:

var p = const ImmutablePoint(2, 2);

两个一样的编译时常量其实是 同一个对象:

var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

assert(identical(a, b)); // They are the same instance!

(12)语法糖
由于把构造函数参数赋值给实例变量的场景太常见了, Dart 提供了一个语法糖来简化这个操作:

class Point {
  num x;
  num y;

  // Syntactic sugar for setting x and y
  // before the constructor body runs.
  Point(this.x, this.y);
}

(13)Named constructors(命名构造函数)
使用命名构造函数可以为一个类实现多个构造函数, 或者使用命名构造函数来更清晰的表明你的意图

class Point {
  num x;
  num y;

  Point(this.x, this.y);

  // Named constructor
  Point.fromJson(Map json) {
    x = json['x'];
    y = json['y'];
  }
}

(14)Invoking a non-default superclass constructor(调用超类构造函数)
默认情况下,子类的构造函数会自动调用超类的 无名无参数的默认构造函数。 超类的构造函数在子类构造函数体开始执行的位置调用。 如果提供了一个 initializer list(初始化参数列表) ,则初始化参数列表在超类构造函数执行之前执行。 下面是构造函数执行顺序:
    initializer list(初始化参数列表)
    superclass’s no-arg constructor(超类的无名构造函数)
    main class’s no-arg constructor(主类的无名构造函数)
如果超类没有无名无参数构造函数, 则你需要手工的调用超类的其他构造函数。 在构造函数参数后使用冒号 (:) 可以调用 超类构造函数。

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).
  Employee.fromJson(Map data) : super.fromJson(findDefaultData()) {
    print('in Employee');
  }

  static findDefaultData(){
    print("argument 22");
  }
  static arguInvoking(){
    print("argument 111");
  }
}

main() {

  var emp = new Employee.fromJson(Employee.arguInvoking());

  (emp as Person).firstName = 'Bob';
  print(emp.firstName);
}

(15)Initializer list(初始化列表)
在构造函数体执行之前除了可以调用超类构造函数之外,还可以 初始化实例参数。 使用逗号分隔初始化表达式。

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)');
  }
}
main() {
  var agru = {
// Keys      Values
    'x' : 111,
    'y': 222,
  };

  var emp = new Point.fromJson(agru);

  print("emp x=${emp.x}, y=${emp.y}");
}

警告: 初始化表达式等号右边的部分不能访问 this。

初始化列表非常适合用来设置 final 变量的值。 下面示例代码中初始化列表设置了三个 final 变量的值。

import 'dart:math';

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);
}

(16)Redirecting constructors(重定向构造函数)
有时候一个构造函数会调动类中的其他构造函数。 一个重定向构造函数是没有代码的,在构造函数声明后,使用 冒号调用其他构造函数。

class Point {
  num x;
  num y;

  // The main constructor for this class.
  Point(this.x, this.y);

  // Delegates to the main constructor.
  Point.alongXAxis(num x) : this(x, 0);
}

(17)Constant constructors(常量构造函数)
如果你的类提供一个状态不变的对象,你可以把这些对象 定义为编译时常量。要实现这个功能,需要定义一个 const 构造函数, 并且声明所有类的变量为 final。

class ImmutablePoint {
  final num x;
  final num y;
  const ImmutablePoint(this.x, this.y);
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);
}

(18)Factory constructors(工厂方法构造函数)
如果一个构造函数并不总是返回一个新的对象,则使用 factory 来定义 这个构造函数。例如,一个工厂构造函数 可能从缓存中获取一个实例并返回,或者 返回一个子类型的实例。
下面代码演示工厂构造函数 如何从缓存中返回对象。

class Logger {
  final String name;
  bool mute = false;

  // _cache is library-private, thanks to the _ in front
  // of its name.
  static final Map<String, Logger> _cache =
      <String, Logger>{};

  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final logger = new Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) {
      print(msg);
    }
  }
}

注意: 工厂构造函数无法访问 this

(19)Instance methods(实例函数)
对象的实例函数可以访问 this。 例如下面示例中的 distanceTo() 函数 就是实例函数:

import 'dart:math';

class Point {
  num x;
  num y;
  Point(this.x, this.y);

  num distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    return sqrt(dx * dx + dy * dy);
  }
}

(20)Getters and setters
Getters 和 setters 是用来设置和访问对象属性的特殊 函数。每个实例变量都隐含的具有一个 getter, 如果变量不是 final 的则还有一个 setter。 你可以通过实行 getter 和 setter 来创建新的属性, 使用 get 和 set 关键字定义 getter 和 setter:

class Rectangle {
  num left;
  num top;
  num width;
  num height;

  Rectangle(this.left, this.top, this.width, this.height);

  // Define two calculated properties: right and 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;
}

main() {
  var rect = new Rectangle(3, 4, 20, 15);
  print("${rect.left}, ${rect.top}, ${rect.width}, ${rect.height} ");
  print("${rect.right}");
  rect.right = 12;
  print("${rect.left}, ${rect.top}, ${rect.width}, ${rect.height} ");
}

getter 和 setter 的好处是,你可以开始使用实例变量,后来 你可以把实例变量用函数包裹起来,而调用你代码的地方不需要修改。

(21)Abstract classes(抽象类)
使用 abstract 修饰符定义一个 抽象类—一个不能被实例化的类。 抽象类通常用来定义接口, 以及部分实现。如果你希望你的抽象类 是可示例化的,则定义一个 工厂 构造函数。
抽象类通常具有 抽象函数。
下面的类不是抽象的,但是定义了一个抽象函数,这样的类是可以被实例化的:

class SpecializedContainer extends AbstractContainer {
  // ...Define more constructors, fields, methods...

  void updateChildren() {
    // ...Implement updateChildren()...
  }

  // Abstract method causes a warning but
  // doesn't prevent instantiation.
  void doSomething();
}

(22)Implicit interfaces(隐式接口)
每个类都隐式的定义了一个包含所有实例成员的接口, 并且这个类实现了这个接口。如果你想 创建类 A 来支持 类 B 的 api,而不想继承 B 的实现, 则类 A 应该实现 B 的接口。
一个类可以通过 implements 关键字来实现一个或者多个接口, 并实现每个接口定义的 API。 例如:

// A person. The implicit interface contains greet().
class Person {
  // In the interface, but visible only in this library.
  final _name;

  // Not in the interface, since this is a constructor.
  Person(this._name);

  // In the interface.
  String greet(who) => 'Hello, $who. I am $_name.';
}

// An implementation of the Person interface.
class Imposter implements Person {
  // We have to define this, but we don't use it.
  final _name = "";

  String greet(who) => 'Hi $who. Do you know who I am?';
}

greetBob(Person person) => person.greet('bob');

main() {
  print(greetBob(new Person('kathy')));
  print(greetBob(new Imposter()));
}

(23)Using collection literals(使用集合字面量)
List 和 map 字面量也是可以参数化的。 参数化定义 list 需要在中括号之前 添加 <type> , 定义 map 需要在大括号之前 添加 <keyType, valueType>。 如果你需要更加安全的类型检查,则可以使用 参数化定义。下面是一些示例:

var names = <String>['Seth', 'Kathy', 'Lars'];
var pages = <String, String>{
  'index.html': 'Homepage',
  'robots.txt': 'Hints for web robots',
  'humans.txt': 'We are people, not machines'
};

(24)Lazily loading a library(延迟载入库)
Deferred loading (也称之为 lazy loading) 可以让应用在需要的时候再 加载库。 下面是一些使用延迟加载库的场景:
   减少 APP 的启动时间。
   执行 A/B 测试,例如 尝试各种算法的 不同实现。
   加载很少使用的功能,例如可选的屏幕和对话框。
要延迟加载一个库,需要先使用 deferred as 来 导入:

import 'package:deferred/hello.dart' deferred as hello;

当需要使用的时候,使用库标识符调用 loadLibrary() 函数来加载库:

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

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

(25)Typedefs
在 Dart 语言中,方法也是对象。 使用 typedef, 或者 function-type alias 来为方法类型命名, 然后可以使用命名的方法。 当把方法类型赋值给一个变量的时候,typedef 保留类型信息。
下面的代码没有使用 typedef:

class SortedCollection {
  Function compare;

  SortedCollection(int f(Object a, Object b)) {
    compare = f;
  }
}

 // Initial, broken implementation.
 int sort(Object a, Object b) => 0;

main() {
  SortedCollection coll = new SortedCollection(sort);

  // 我们只知道 compare 是一个 Function 类型,
  // 但是不知道具体是何种 Function 类型?
  assert(coll.compare is Function);
}

当把 f 赋值给 compare 的时候, 类型信息丢失了。 f 的类型是 (Object, Object) → int (这里 → 代表返回值类型), 当然该类型是一个 Function。如果我们使用显式的名字并保留类型信息, 开发者和工具可以使用 这些信息:

typedef int Compare(Object a, Object b);

class SortedCollection {
  Compare compare;

  SortedCollection(this.compare);
}

 // Initial, broken implementation.
 int sort(Object a, Object b) => 0;

main() {
  SortedCollection coll = new SortedCollection(sort);
  assert(coll.compare is Function);
  assert(coll.compare is Compare);
}

(26)Metadata(元数据)
使用元数据给你的代码添加其他额外信息。 元数据注解是以 @ 字符开头,后面是一个编译时 常量(例如 deprecated)或者 调用一个常量构造函数。
有三个注解所有的 Dart 代码都可以使用: @deprecated、 @override、 和 @proxy。关于 @override 和 @proxy 示例请参考 Extending a class。 下面是使用 @deprecated 的 示例:

class Television {
  /// _Deprecated: Use [turnOn] instead._
  @deprecated
  void activate() {
    turnOn();
  }

  /// Turns the TV's power on.
  void turnOn() {
    print('on!');
  }
}


你还可以定义自己的元数据注解。 下面的示例定义了一个带有两个参数的 @todo 注解:

library todo;

class todo {
  final String who;
  final String what;

  const todo(this.who, this.what);
}


使用 @todo 注解的示例:

import 'todo.dart';

@todo('seth', 'make this do something')
void doSomething() {
  print('do something');
}


元数据可以在 library、 class、 typedef、 type parameter、 constructor、 factory、 function、 field、 parameter、或者 variable 声明之前使用,也可以在 import 或者 export 指令之前使用。 使用反射可以在运行时获取元数据 信息。

(27) dynamicObject

Object 是Dart所有对象的根基类,也就是说所有类型都是Object的子类(包括Function和Null),所以任何类型的数据都可以赋值给Object声明的对象. dynamicvar一样都是关键词,声明的变量可以赋值任意对象。 而dynamicObject相同之处在于,他们声明的变量可以在后期改变赋值类型。

 dynamic t;
 Object x;
 t = "hi world";
 x = 'Hello Object';
 //下面代码没有问题
 t = 1000;
 x = 1000;

dynamicObject不同的是,dynamic声明的对象编译器会提供所有可能的组合, 而Object声明的对象只能使用Object的属性与方法, 否则编译器会报错。如:

 dynamic a;
 Object b;
 main() {
     a = "";
     b = "";
     printLengths();
 }   

 printLengths() {
     // no warning
     print(a.length);
     // warning:
     // The getter 'length' is not defined for the class 'Object'
     print(b.length);
 }

变量a不会报错, 变量b编译器会报错

dynamic的这个特性与Objective-C中的id作用很像. dynamic的这个特点使得我们在使用它时需要格外注意,这很容易引入一个运行时错误

(28)Stream

Stream 也是用于接收异步事件数据,和Future 不同的是,它可以接收多个异步操作的结果(成功或失败)。 也就是说,在执行异步任务时,可以通过多次触发成功或失败事件来传递结果数据或错误异常。 Stream 常用于会多次读取数据的异步任务场景,如网络内容下载、文件读写等。举个例子:

Stream.fromFutures([
  // 1秒后返回结果
  Future.delayed(new Duration(seconds: 1), () {
    return "hello 1";
  }),
  // 抛出一个异常
  Future.delayed(new Duration(seconds: 2),(){
    throw AssertionError("Error");
  }),
  // 3秒后返回结果
  Future.delayed(new Duration(seconds: 3), () {
    return "hello 3";
  })
]).listen((data){
   print(data);
}, onError: (e){
   print(e.message);
},onDone: (){

});

上面的代码依次会输出:

I/flutter (17666): hello 1
I/flutter (17666): Error
I/flutter (17666): hello 3

代码很简单,就不赘述了。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值