Dart基础介绍
Nealyang opened this issue · 0 comments
前言
Flutter的开发语言是Dart,就类似Android的开发语言是java一样,这个不需要过多介绍,本章节我们简单介绍下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之旅吧~