1、前言:
最近在学习Flutter,作为Flutter的编程语言,Dart语言语法的学习自然就编程重中之重。本文主要记录自己在学习Dart语法时的一些重要概念和学习路线,在学习官方文档时,对自己觉得重要的或者需要标记的语法知识点进行汇总和整理而成,方便后续进行语法整体概念的回顾和复习。如果同时能对其他人有所帮助那就更好不过了,同时也建议对Dart感兴趣的同学,去查看Dart语法的官方文档。
2、 Important concepts(重要的概念)
所有能够使用变量引用的都是对象, 每个对象都是一个类的实例。在 Dart 中 甚至连 数字、方法和
null
都是对象。所有的对象都继承于 Object类。使用静态类型(例如前面示例中的
num
) 可以更清晰的表明你的意图,并且可以让静态分析工具来分析你的代码, 但这并不是牵制性的。(在调试代码的时候你可能注意到 没有指定类型的变量的类型为dynamic
。)Dart 在运行之前会先解析你的代码。你可以通过使用 类型或者编译时常量来帮助 Dart 去捕获异常以及 让代码运行的更高效。
Dart 支持顶级方法 (例如
main()
),同时还支持在类中定义函数。 (静态函数和实例函数)。 你还可以在方法中定义方法 (嵌套方法或者局部方法)。同样,Dart 还支持顶级变量,以及 在类中定义变量(静态变量和实例变量)。 实例变量有时候被称之为域(Fields)或者属性(Properties)。
和 Java 不同的是,Dart 没有
public
、protected
、 和private
关键字。如果一个标识符以 (_) 开头,则该标识符 在库内是私有的。详情请参考:库和可见性。标识符可以以字母或者 _ 下划线开头,后面可以是 其他字符和数字的组合。
有时候 表达式 expression 和 语句 statement 是有区别的,所以这种情况我们会分别指明每种情况。
Dart 工具可以指出两种问题:警告和错误。 警告只是说你的代码可能有问题, 但是并不会阻止你的代码执行。 错误可以是编译时错误也可以是运行时错误。遇到编译时错误时,代码将 无法执行;运行时错误将会在运行代码的时候导致一个异常。
3、 Keywords(关键字)
1 带有上标 1 的关键字是 内置关键字。避免把内置关键字当做标识符使用。 也不要把内置关键字 用作类名字和类型名字。 有些内置关键字是为了方便把 JavaScript 代码移植到 Dart 而存在的。 例如,如果 JavaScript 代码中有个变量的名字为
factory
, 在移植到 Dart 中的时候,你不必重新命名这个变量。2 带有上标 2 的关键字,是在 Dart 1.0 发布以后又新加的,用于 支持异步相关的特性。 你不能在标记为
async
、async*
、或者sync*
的方法体内 使用async
、await
、或者yield
作为标识符。 详情请参考:异步支持。所以其他单词都是 保留词。 你不能用保留词作为关键字。
4、Variables(变量)
Default value(默认值)
- 未初始化的变量默认值为null,数字也是对象,未初始化的数字默认值也为null
- Optional types(可选的类型)
> var name = 'Bob';
> String name = 'Bob';
> 即声明变量时,既可以显式声明其类型,也可以不声明其类型。对于局部变量,推荐使用var,即不指明其类型
Final and const
- 如果你以后不打算修改一个变量,使用 final 或者 const。 一个 final 变量只能赋值一次;一个 const 变量是编译时常量。 (Const 变量同时也是 final 变量。) 顶级的 final 变量或者类中的 final 变量在 第一次使用的时候初始化。
- 实例变量可以为 final 但是不能是 const 。
- const 关键字不仅仅只用来定义常量。 有可以用来创建不变的值, 还能定义构造函数为 const 类型的,这种类型 的构造函数创建的对象是不可改变的。任何变量都可以有一个不变的值。
// Note: [] creates an empty list.
// const [] creates an empty, immutable list (EIA).
var foo = const []; // foo is currently an EIA.
final bar = const []; // bar will always be an EIA.
const baz = const []; // baz is a compile-time constant EIA.
// You can change the value of a non-final, non-const variable,
// even if it used to have a const value.
foo = [];
// You can't change the value of a final or const variable.
// bar = []; // Unhandled exception.
// baz = []; // Unhandled exception.
5、Built-in types(内置的类型)
Dart 内置支持下面这些类型:
- numbers
- strings
- booleans
- lists (也被称之为 arrays)
- maps
- runes (用于在字符串中表示 Unicode 字符)
- symbols
Numbers(数值)
- int : 整数值,其取值通常位于 -253 和 253 之间。
- double : 64-bit (双精度) 浮点数,符合 IEEE 754 标准。
下面是字符串和数字之间转换的方式:
// 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');
Strings(字符串)
可以在字符串中使用表达式,用法是这样的: ${expression}
var s = 'string interpolation';
assert('Dart has $s, which is very handy.' ==
'Dart has string interpolation, ' +
'which is very handy.');
assert('That deserves all caps. ' +
'${s.toUpperCase()} is very handy!' ==
'That deserves all caps. ' +
'STRING INTERPOLATION is very handy!');
使用三个单引号或者双引号也可以 创建多行字符串对象:
var s1 = '''
You can create
multi-line strings like this one.
''';
var s2 = """This is also a
multi-line string.""";
通过提供一个 r 前缀可以创建一个 “原始 raw” 字符串:
var s = r"In a raw string, even \n isn't special.";
Booleans(布尔值)
var name = 'Bob';
if (name) {
// Prints in JavaScript, not in Dart.
print('You have a name!');
}
Dart 有一个名字为 bool 的类型。 只有两个对象是布尔类型的:true 和 false 所创建的对象, 这两个对象也都是编译时常量。当 Dart 需要一个布尔值的时候,只有 true 对象才被认为是 true。 所有其他的值都是 flase。这点和 JavaScript 不一样,在 JavaScript 中 name 是非 null 对象所以认为是 true
Lists(列表)
var list = [1, 2, 3];
assert(list.length == 3);
assert(list[1] == 2);
list[1] = 1;
assert(list[1] == 1);
Maps
var gifts = {
// Keys Values
'first' : 'partridge',
'second': 'turtledoves',
'fifth' : 'golden rings'
};
var nobleGases = {
// Keys Values
2 : 'helium',
10: 'neon',
18: 'argon',
};
var gifts = new Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
var nobleGases = new Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds'; // Add a key-value pair
Runes
在 Dart 中,runes 代表字符串的 UTF-32 code points。
Symbols
使用 Symbol 字面量来获取标识符的 symbol 对象,也就是在标识符 前面添加一个 # 符号:
#radix
#bar
Symbol 字面量定义是编译时常量。
6、Functions(方法)
Dart 是一个真正的面向对象语言,方法也是对象并且具有一种 类型, Function
。 这意味着,方法可以赋值给变量,也可以当做其他方法的参数。 也可以把 Dart 类的实例当做方法来调用。
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
Optional parameters(可选参数)
- Optional named parameters(可选命名参数)
/// Sets the [bold] and [hidden] flags to the values
/// you specify.
enableFlags({bool bold, bool hidden}) {
// ...
}
- Optional positional parameters(可选位置参数)
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
assert(say('Bob', 'Howdy', 'smoke signal') ==
'Bob says Howdy with a smoke signal');
- Default parameter values(默认参数值)
/// Sets the [bold] and [hidden] flags to the values you
/// specify, defaulting to false.
void enableFlags({bool bold = false, bool hidden = false}) {
// ...
}
// bold will be true; hidden will be false.
enableFlags(bold: true);
String say(String from, String msg,
[String device = 'carrier pigeon', String mood]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
if (mood != null) {
result = '$result (in a $mood mood)';
}
return result;
}
assert(say('Bob', 'Howdy') ==
'Bob says Howdy with a carrier pigeon');
The main() function(入口函数)
没啥要说的
Functions as first-class objects(一等方法对象)
可以把方法当做参数调用另外一个方法。例如:
printElement(element) {
print(element);
}
var list = [1, 2, 3];
// Pass printElement as a parameter.
list.forEach(printElement);
方法也可以赋值给一个变量:
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
Anonymous functions(匿名方法)
下面的代码定义了一个参数为i (该参数没有指定类型)的匿名函数。
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));
Lexical scope(静态作用域)
略
Lexical closures(词法闭包)
一个 闭包 是一个方法对象,不管该对象在何处被调用, 该对象都可以访问其作用域内 的变量。
/// 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);
}
Testing functions for equality(测试函数是否相等)
强调: 是判断函数是否相等,不是对象,这里不再说明。
Return values(返回值)
所有的函数都返回一个值。如果没有指定返回值,则 默认把语句 return null;
作为函数的最后一个语句执行。
7、Operators(操作符)
Arithmetic operators(算术操作符)
assert(2 + 3 == 5);
assert(2 - 3 == -1);
assert(2 * 3 == 6);
assert(5 / 2 == 2.5); // Result is a double
assert(5 ~/ 2 == 2); // Result is an integer
assert(5 % 2 == 1); // Remainder
print('5/2 = ${5~/2} r ${5%2}'); // 5/2 = 2 r 1
Equality and relational operators(相等相关的操作符)
assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);
Type test operators(类型判定操作符)
if (emp is Person) { // Type check
emp.firstName = 'Bob';
}
(emp as Person).firstName = 'Bob';
Assignment operators(赋值操作符)
a = value; // 给 a 变量赋值
b ??= value; // 如果 b 是 null,则赋值给 b;
// 如果不是 null,则 b 的值保持不变
Logical operators(逻辑操作符)
略
Bitwise and shift operators(位和移位操作符)
略
Conditional expressions(条件表达式)
var finalStatus = m.isFinal ? 'final' : 'not final';
Cascade notation (..)(级联操作符)
querySelector('#button') // Get an object.
..text = 'Confirm' // Use its members.
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
Other operators(其他操作符)
8、Control flow statements(流程控制语句)
可以使用下面的语句来控制 Dart 代码的流程:
- if and else
- for loops
- while and do-while loops
- break and continue
- switch and case
- assert
var callbacks = [];
for (var i = 0; i < 2; i++) {
callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());
candidates.forEach((candidate) => candidate.interview());
9、Exceptions(异常)
Throw Catch Finally
distanceTo(Point other) =>
throw new UnimplementedError();
对于可以抛出多种类型异常的代码,你可以指定 多个捕获语句。每个语句分别对应一个异常类型, 如果捕获语句没有指定异常类型,则 该可以捕获任何异常类型:
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 来 捕获异常对象。
try {
breedMoreLlamas();
} catch(e) {
print('Error: $e'); // Handle the exception first.
} finally {
cleanLlamaStalls(); // Then clean up.
}
10、Classes
Instance variables
略
Constructors
class Point {
num x;
num y;
// Syntactic sugar for setting x and y
// before the constructor body runs.
Point(this.x, this.y);
}
Default constructors(默认构造函数)
如果你没有定义构造函数,则会有个默认构造函数。 默认构造函数没有参数,并且会调用超类的 没有参数的构造函数。
Constructors aren’t inherited(构造函数不会继承)
子类不会继承超类的构造函数。 子类如果没有定义构造函数,则只有一个默认构造函数 (没有名字没有参数)。
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'];
}
}
-
Invoking a non-default superclass constructor(调用超类构造函数)
构造函数执行顺序:
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(data) {
print('in Employee');
}
}
main() {
var emp = new Employee.fromJson({});
// Prints:
// in Person
// in Employee
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
(emp as Person).firstName = 'Bob';
}
Initializer list(初始化列表)
在构造函数体执行之前除了可以调用超类构造函数之外,还可以 初始化实例参数。 使用逗号分隔初始化表达式。
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);
}
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);
}
Constant constructors(常量构造函数)
class ImmutablePoint {
final num x;
final num y;
const ImmutablePoint(this.x, this.y);
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
}
Factory constructors(工厂方法构造函数)
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);
}
}
}
var logger = new Logger('UI');
logger.log('Button clicked');
Methods(函数)
-
Instance methods(实例函数)
略
-
Getters and setters
略
-
Abstract methods(抽象函数)
略
-
Overridable operators(可覆写的操作符)
class Vector {
final int x;
final int y;
const Vector(this.x, this.y);
/// Overrides + (a + b).
Vector operator +(Vector v) {
return new Vector(x + v.x, y + v.y);
}
/// Overrides - (a - b).
Vector operator -(Vector v) {
return new Vector(x - v.x, y - v.y);
}
}
main() {
final v = new Vector(2, 3);
final w = new Vector(2, 2);
// v == (2, 3)
assert(v.x == 2 && v.y == 3);
// v + w == (4, 5)
assert((v + w).x == 4 && (v + w).y == 5);
// v - w == (0, 1)
assert((v - w).x == 0 && (v - w).y == 1);
}
Abstract classes(抽象类)
略
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()));
}
Extending a class(扩展类)
略
Enumerated types(枚举类型)
略
Adding features to a class: mixins(为类添加新的功能)
Mixins 是一种在多类继承中重用 一个类代码的方法。
使用 with 关键字后面为一个或者多个 mixin 名字来使用 mixin。 下面是示例显示了如何使用 mixin:
class Musician extends Performer with Musical {
// ...
}
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
定义一个类继承 Object,该类没有构造函数, 不能调用 super ,则该类就是一个 mixin。例如:
abstract class Musical {
bool canPlayPiano = false;
bool canCompose = false;
bool canConduct = false;
void entertainMe() {
if (canPlayPiano) {
print('Playing piano');
} else if (canConduct) {
print('Waving hands');
} else {
print('Humming to self');
}
}
}
Class variables and methods(类变量和函数)
使用 static 关键字来实现类级别的变量和函数。
Static variables(静态变量)
Static methods(静态函数)
11、Generics(泛型)
Generic collections and the types they contain
Dart 的泛型类型是固化的,在运行时有也 可以判断具体的类型。例如在运行时(甚至是成产模式) 也可以检测集合里面的对象类型:
var names = new List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
Using generic methods(使用泛型函数)
一开始,泛型只能在 Dart 类中使用。 新的语法也支持在函数和方法上使用泛型了。
T first<T>(List<T> ts) {
// ...Do some initial work or error checking, then...
T tmp ?= ts[0];
// ...Do some additional checking or processing...
return tmp;
}
12、Libraries and visibility(库和可见性)
使用 import 和 library 指令可以帮助你创建 模块化的可分享的代码。库不仅仅提供 API, 还是一个私有单元:以下划线 (_) 开头的标识符只有在库 内部可见。每个 Dart app 都是一个库, 即使没有使用 library 命令也是一个库。
Using libraries(使用库)
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.
Importing only part of a library(导入库的一部分)
// Import only foo.
import 'package:lib1/lib1.dart' show foo;
// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
Lazily loading a library(延迟载入库)
要延迟加载一个库,需要先使用 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。
Implementing libraries(实现库)
13、Asynchrony support(异步支持)
checkVersion() async {
var version = await lookUpVersion();
if (version == expectedVersion) {
// Do something.
} else {
// Do something else.
}
}
14、Callable classes(可调用的类)
如果 Dart 类实现了 call() 函数则 可以当做方法来调用。
在下面的示例中,WannabeFunction 类定义了一个 call() 方法,该方法有三个字符串参数,并且返回三个字符串 串联起来的结果。
class WannabeFunction {
call(String a, String b, String c) => '$a $b $c!';
}
main() {
var wf = new WannabeFunction();
var out = wf("Hi","there,","gang");
print('$out');
}
15、Isolates
略
16、Typedefs
在 Dart 语言中,方法也是对象。 使用 typedef, 或者 function-type alias 来为方法类型命名, 然后可以使用命名的方法。 当把方法类型赋值给一个变量的时候,typedef 保留类型信息。
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);
}
由于 typedefs 只是别名,他们还提供了一种 判断任意 function 的类型的方法。例如:
typedef int Compare(int a, int b);
int sort(int a, int b) => a - b;
main() {
assert(sort is Compare); // True!
}
17、Metadata(元数据)
使用元数据给你的代码添加其他额外信息。 元数据注解是以 @ 字符开头,后面是一个编译时 常量(例如 deprecated)或者 调用一个常量构造函数。
有三个注解所有的 Dart 代码都可以使用: @deprecated、 @override、 和 @proxy。
定义自己的元数据注解:
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 指令之前使用。 使用反射可以在运行时获取元数据 信息。
18、Comments(注释)
Single-line comments
Multi-line comments
Documentation comments
/// A domesticated South American camelid (Lama glama).
///
/// Andean cultures have used llamas as meat and pack
/// animals since pre-Hispanic times.
class Llama {
String name;
/// Feeds your llama [Food].
///
/// The typical llama eats one bale of hay per week.
void feed(Food food) {
// ...
}
/// Exercises your llama with an [activity] for
/// [timeLimit] minutes.
void exercise(Activity activity, int timeLimit) {
// ...
}
}
19、学习资料
对Dart感兴趣的同学请查看 Dart官方文档,进行学习和参考。