Nealyang/PersonalBlog

Dart基础介绍

Nealyang opened this issue · 0 comments

前言

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之旅吧~