Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dart基础介绍 #18

Open
Nealyang opened this issue Mar 19, 2019 · 0 comments
Open

Dart基础介绍 #18

Nealyang opened this issue Mar 19, 2019 · 0 comments
Labels

Comments

@Nealyang
Copy link
Owner

前言

Flutter的开发语言是Dart,就类似Android的开发语言是java一样,这个不需要过多介绍,本章节我们简单介绍下Dart中的一些语法,篇幅原因,不会谈及太深。笔者建议,官方Api浏览一遍即可,重在实操。涉及到不明白或者需要去查的部分可以再去翻阅文档。

这里我们会重点说下

文档推荐

Dart中文文档

Dart 官网

Dart api

关键概念

  • Dart中所有东西都是对象,每一个对象是类的实例,无论是数字、字符串、类还是函数
  • Dart是强类型语言,但是也只是var声明,因为Dart会推断变量类型
  • Dart支持通用类型,比如List<Map<String,String>>也可以写成List<Map<String,dynamic>>
  • Dart运行冲main函数开始,支持绑定到类或者对象的函数
  • Dart没有空开、私有的关键字,但是可以通过对象里下划线_开头去定义一个变量,则为私有变量

数据类型与修饰符

Dart语言支持如下数据类型

  • number
  • string
  • boolean
  • list
  • map
  • rune
  • symbol
int i  = 1;
double d = 1.1;
double e = 1.42e5;
// 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');

var s = 'string interpolation';
//创建多行字符串的方法:使用带有单引号或双引号的三重引号:
var s2 = """This is also a
multi-line string.""";

//只有两个对象具有bool类型:布尔字面量true和false,它们都是编译时常量
bool isLogin = false;
var hasMore = true;

//在Dart中,数组是列表对象
var list = [1, 2, 3];
//创建一个编译时常量列表,要在列表字面量之前添加const
List<int> constantList = const [1, 2, 3];
// constantList[1] = 1; // Uncommenting this causes an error.

// Map 在Dart中不是映射,而是对象
Map gifts = {
  // Key:    Value
  'first': 'partridge',
  'second': 'turtledoves',
  'fifth': 'golden rings'
};

Map gifts = Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';

函数

Dart是一种真正的面向对象语言,所以即使函数也是对象,具有类型和功能。这意味着函数可以分配给变量或作为参数传递给其他函数。您还可以像调用函数一样调用Dart类的实例

bool isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}
// 官方推荐标明返回值类型,我们也可以不写.当然,我们也可以使用箭头函数
isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

可选参数

可选参数可以是位置参数,也可以是命名参数,但不能两者都是。

void enableFlags({bool bold, bool hidden}) {...}
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;
}

默认参数值

void enableFlags({bool bold = false, bool hidden = false}) {...}
//bold 为true,hidden为false
enableFlags(bold: true);

词法作用域和闭包

bool topLevel = true;

void main() {
  var insideMain = true;

  void myFunction() {
    var insideFunction = true;

    void nestedFunction() {
      var insideNestedFunction = true;

      assert(topLevel);
      assert(insideMain);
      assert(insideFunction);
      assert(insideNestedFunction);
    }
  }
}

使用过es6的前端同学对这个应该很好理解

运算符和流程控制语句这里就省略了

类与泛型

基础知识

实用类成员

Point p = Point(2,2);//new 可有可无 -> Dart2
p.y = 3;//set
assert(p.y == 3);//get
//为避免最左操作数为空时出现异常,使用 ?.代替 .来使用:
p?.y = 4;

构造函数

class Point {
  num x;
  num y;

  Point(this.x, this.y);

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

获取对象类型

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

构造函数

通过创建一个与类同名的函数来声明构造函数.构造函数最常见的应用形式是使用构造函数生成一个类的新实例.

如果不声明构造函数,则为您提供默认构造函数。默认构造函数没有参数,并在超类中调用无参数构造函数。子类不从父类继承构造函数。没有声明构造函数的子类只有默认的构造函数(没有参数,没有名称)而不是从父类继承的构造函数。

命名构造函数

使用命名构造函数可以在一个类中定义多个构造函数,或者让一个类的作用对于开发人员来说更清晰

class Point {
  num x, y;

  Point(this.x, this.y);

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

一定要记住构造函数是不会从父类继承的,这意味着父类的命名构造函数子类也不会继承。如果你希望使用在超类中定义的命名构造函数来创建子类,则必须在子类中实现该构造函数。

初始化列表

在构造函数主体运行之前初始化实例变量。初始值设定项用逗号分开。注意初始化器的右边部分中无法访问this关键字。
在开发期间,可以通过在初始化列表中使用assert来验证输入。

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

///运行结果
3.605551275463989

常亮构造函数

如果类生成的对象不会改变,您可以使这些对象成为编译时常量。为此,定义一个const构造函数,并确保所有实例变量都是final的。

class ImmutablePoint {
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);

  final num x, y;

  const ImmutablePoint(this.x, this.y);
}

工厂构造函数

在实现构造函数时使用factory关键字,该构造函数并不总是创建类的新实例。例如,工厂构造函数可以从缓存返回实例,也可以返回子类型的实例.这在我们后面介绍定义model很有用

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 = Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}
//工厂构造函数不能访问this关键字。调用工厂构造函数,就像调用其他构造函数一样:
Logger logger = Logger('UI');
logger.log('Button clicked');

方法

import 'dart:math';

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

  Rectangle(this.left, this.top, this.width, this.height);
  
  //实例方法
  num distanceTo(Rectangle other) {
    var dx = width - other.width;
    var dy = height - other.height;
    return sqrt(dx * dx + dy * dy);
  }

  //set get
  // 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;
}

void main() {
  var rect = Rectangle(3, 4, 20, 15);
  assert(rect.left == 3);
  rect.right = 12;
  assert(rect.left == -8);
}

枚举类型

//使用enum关键字声明一个枚举类型:
enum Color { red, green, blue }
//枚举中的每个值都有一个索引getter,它返回enum声明中值的从0开始的位置
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
//要获取枚举中所有值的列表,请使用enum的values 常量
List<Color> colors = Color.values;
assert(colors[2] == Color.blue);
//您可以在switch语句中使用enum,如果switch的case不处理enum的所有值,将会报一个警告消息:
var aColor = Color.blue;

switch (aColor) {
  case Color.red:
    print('Red as roses!');
    break;
  case Color.green:
    print('Green as grass!');
    break;
  default: // 比如得有default,否则 WARNING.
    print(aColor); // 'Color.blue'
}

泛型

泛型通常是类型安全所必需的,他们对于写出严谨高质量的代码是很有用的:

  • 适当地指定泛型类型可以生成更好的代码
  • 可以使用泛型来减少代码重复
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
var nameSet = Set<String>.from(names);

var views = Map<int, View>();

限制参数化类型

在实现泛型类型时,您可能希望限制其参数的类型。你可以使用extends。

class Foo<T extends SomeBaseClass> {
  // Implementation goes here...
  String toString() => "Instance of 'Foo<$T>'";
}

class Extender extends SomeBaseClass {...}
//可以使用SomeBaseClass 或它的任何子类作为泛型参数:
var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();

库与异步

import和library指令可以帮助您创建模块化和可共享的代码库。使用import来指定如何在另一个库的范围中使用来自一个库的命名空间。

导入一个库仅仅需要提供库的URI。对于内置库,URI具有特定的形式(dart:scheme)。对于其他库,可以使用文件路径或者包:scheme的形式。包:scheme形式指定包管理器(如pub工具)提供的库。

import 'dart:html';
import 'package:test/test.dart';

//指定一个库前缀 as
import 'package:lib2/lib2.dart' as lib2;
//如果您只想使用库的一部分,您可以有选择地导入库
// 只导入foo.
import 'package:lib1/lib1.dart' show foo;

// 除了foo 都导入.
import 'package:lib2/lib2.dart' hide foo;

懒加载库

延迟加载(也称为懒加载)允许应用程序在需要时按需加载库。以下是一些您可能使用延迟加载的情况:

  • 减少应用程序的初始启动时间
  • 例如,要执行A/B测试——尝试算法的其他实现
  • 加载很少使用的功能,如可选屏幕和对话框

要延迟加载库,必须首先使用deferred as进行导入。

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

当您需要库时,使用库的标识符调用loadLibrary()。

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

注意:

  • 在导入文件中,递延库的常量不是常量。记住,这些常量在延迟库加载之前是不存在的。
  • 不能在导入文件中使用来自延迟库的类型
  • Dart隐式地将loadLibrary()插入到你定义使用deferred作为名称空间的名称空间中。函数的作用是:返回一个Future。

异步

async和await关键字支持异步编程,允许您编写类似于同步代码的异步代码。

处理Futures

当你需要一个完整的Futures的结果时,你有两个选择:

  • 使用async和await
  • 使用Future的API

使用async和await的代码虽然是异步的,但是看起来很像同步代码。要使用await必须是对一个使用async标注的异步函数:

Future checkVersion() async {
  try {//使用try,catch和finally来处理使用await的代码中的错误
    version = await lookUpVersion();
  } catch (e) {
    // React to inability to look up the version
  }
}

可以在异步函数中多次使用await

var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);

在await表达式中,表达式的值通常是一个Future对象。如果不是,那么这个值将被自动包装成Future。Futrue对象指示返回结果一定是一个对象。表达式的值就是被返回的对象。await表达式会让程序执行挂起,直到返回的对象可用。

结束语

以上大致总结与Dart中文文档,关于本项目主要涉及到的知识这里都有说明,但是并不包括Dart的全部知识,感兴趣的同学可以去官网学习学习,当然,我更喜欢的是,在项目中学习~ 下面让我们开始FLutter之旅吧~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant