tour
major feature, from variables and operators to classes and libraries
basic program
// Define a function.
printInteger(int aNumber) {
print('The number is $aNumber.'); // Print to console.
}
// This is where the app starts executing.
main() {
var number = 42; // Declare and initialize a variable.
printInteger(number); // Call a function.
}
//
:单行注释int
:内置类型print
:输出到控制台的便捷方法'...'、"..."
:字符串字面量$variableName、${expression}
:字符串插值main()
:特殊的、必须的顶级函数,程序开始的地方var
:声明变量而不指定其类型42
:数字字面量,编译时常量
important concepts
- 所有可赋值给变量的都是对象,所有对象都是类的实例,甚至数字、函数和
null
都是对象,所有对象继承自 Object - 尽管 Dart 是强类型语言,但是类型声明是可选的(
var
),因为 Dart 可以进行类型推断;使用dynamic
显示声明不期望任何类型 - 支持泛型,如
List<int>
表示整数列表、List<dynamic>
表示任意类型的对象列表 - 支持顶级/全局函数,也可以创建内嵌/本地函数,同样支持静态、实例方法
- 支持顶级/全局变量,同样支持静态、实例变量(域/属性)
- 没有
public
、protected
、private
关键字,下划线(_
)前缀的变量属于库私有
Variables
创建并初始化变量:
var name = 'Bob';
变量存储引用。name
变量存储了值为“Bob”的String
对象的引用
name
变量的类型被推断为String
,如果不被限制为单一类型,可以指定为 Object
、dynamic
:
dynamic name = 'Bob';
或者明确声明类型:
String name = 'Bob';
对于局部变量,优先使用
var
而不是明确声明
Default value
未经初始化的变量,初始化值为 null
,甚至数值类型也是这样,因为它们是对象。
int lineCount;
assert(lineCount == null);
生产中,assert() 代码被忽略、不执行,然而,在开发中,如果 condation 为 false,执行 assert(condation) 时将抛出异常
Final and const
如果从未打算更改变量的值,使用 final
、const
,而不是 var
。
- final 变量只能被设置一次,const 变量是编译时常量(隐式 final)
- final 顶级/全局变量、类/静态变量在第一次使用时被初始化
- 实例变量可以为 final 而不能为 const, 且必须在构造器的主体开始之前被初始化
- const 变量如果为类级别,必须标记为
static const
- 编译时常量包括数字/字符串等字面量、const 变量以及对常量数字进行算术操作
- ++使用
const
创建常量值以及声明能够创建常量值的构造器++
Build-in types
Numbers
int
不大于 64 bits,取决于平台:
- 运行在 Dart VM ——
$-2^{63} ~ 2^{63} - 1$
- 编译成 JavaScript ——
$-2^{53} ~ 2^{53} - 1$
double
64位(双精度)浮点数
int
和double
都是num
的子类,num 包含基本算术操作符和数学计算方法,位操作定义在 int 中,dart:math
库中定义更多数学计算
数字与字符串相互转换:
// String -> int
var one = int.parse('1');
// String -> double
var onePointOne = double.parse('1.1');
// int -> String
String oneAsString = 1.toString();
// double -> String
String piAsString = 3.14159.toStringAsFixed(2);
数字字面量是编译时常量,算术表达式的操作数都为编译时常量时整个表达式也为编译时常量:
const msPerSecond = 1000;
const secondsUntilRetry = 5;
const msUntilRetry = secondsUntilRetry * msPerSecond;
Strings
UTF-16 编码单元的序列,可以使用双引号或单引号
var s1 = 'Single quotes work well for string literals.';
var s2 = "Double quotes work just as well.";
var s3 = 'It\'s easy to escape the string delimiter.';
var s4 = "It's even easier to use the other delimiter.";
使用 ${expression}
将表达式的值放到字符串中,如果表达式是变量标识符可以去掉 {}
,调用对象的 toString
方法来获取该对象对应的字符串值。
==
判断两个字符串内容是否相等,+
连接两个字符串(或者两个字符串字面量相邻),三引号创建多行字符串,r
前缀创建“原生”字符串。
字符串字面量是编译时常量,当插值表达式为编译时常量时,要求其中操作数也为编译时常量且类型为 bool
/ num
/ String
/ null
// These work in a const string.
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';
// These do NOT work in a const string.
var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = [1, 2, 3];
const validConstString = '$aConstNum $aConstBool $aConstString';
// const invalidConstString = '$aNum $aBool $aString $aConstList';
Booleans
布尔类型为 bool
,字面量为 true
、false
(编译时常量)。
Lists
数组即 List,其字面量示例:
var list = [1, 2, 3];
推断类型为 List<int>
,如果试图添加非 int
对象,分析器或运行时将会报错。
在 List 字面量前添加const
,创建其编译时常量:
var constantList = const [1, 2, 3];
// constantList[1] = 1; // Uncommenting this causes an error.
使用 ...
将一个 List 中所有元素插入到另一个 List 中:
var list = [1, 2, 3];
var list2 = [0, ...list];
assert(list2.length == 4);
使用 ...?
避免插入为 null
的 List 时报错:
var list;
var list2 = [0, ...?list];
assert(list2.length == 1);
创建 List 时使用条件if
:
var nav = [
'Home',
'Furniture',
'Plants',
if (promoActive) 'Outlet'
];
创建 List 时使用循环for
:
var listOfInts = [1, 2, 3];
var listOfStrings = [
'#0',
for (var i in listOfInts) '#$i'
];
assert(listOfStrings[1] == '#1');
Sets
元素不重复的无序的集合,使用 Set 字面量示例:
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
推断类型为Set<String>
。
创建空的 Set:
var names = <String>{};
// Set<String> names = {};
// var names = {}; // Creates a map, not a set.
Maps
键和值可以是任意类型的对象,使用 Map 字面量示例:
var gifts = {
// Key: Value
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings'
};
var nobleGases = {
2: 'helium',
10: 'neon',
18: 'argon',
};
使用 Map 构造器示例:
var gifts = Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
var nobleGases = Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';
Operators
作用与两个操作数的操作符取决于最左侧的操作数
==
判断两个对象的内容是否一样,identical()
判断两个对象是否为同一个
x == y
运行方式如下:
- 如果 x、y 都是
null
表达式的值为true
,如果只有一个为null
,表达式的值则为false
- 表达式的值等于
x.==(y)
,操作符==
其实是由第一个操作数所调用的方法,甚至很多操作符都可以被重写
Type test
Operator | Meaning |
---|---|
as | Typecast (also used to specify library prefixes) |
is | True if the object has the specified type |
is! | False if the object has the specified type |
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
(emp as Person).firstName = 'Bob';
如果 emp
为 null
或者不是 Person 类型时,is
示例代码将不执行赋值操作,然而,as
示例代码将会抛出异常。
Assignment
??=
只在被赋值变量为 null
时才执行赋值操作:
// Assign value to a
a = value;
// Assign value to b if b is null; otherwise, b stays the same
b ??= value;
Conditional expressions
expr1 ?? expr2
如果 expr1 不为 null
返回其值,否者,返回 expr2 的值。
Cascade notation (..)
在同一对象上执行一系列操作,除了方法调用,还能访问属性,这样就节省了创建临时变量的步骤、使代码更流畅。
querySelector('#confirm') // Get an object.
..text = 'Confirm' // Use its members.
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
var sb = StringBuffer();
sb.write('foo')
..write('bar'); // Error: method 'write' isn't defined for 'void'.
严格讲,
..
级联符号不是一种操作符而是 Dart 的一种语法
Other
?.
(Conditional member access): Like .
, but the leftmost operand can be null
; for example, foo?.bar
selects property bar from expression foo unless foo is null
(in which case the value of foo?.bar
is null
)
Control flow
if
andelse
for
loopswhile
anddo-while
loopsbreak
andcontinue
switch
andcase
assert
for
循环中的闭包可以获取到索引值:
var callbacks = [];
for (var i = 0; i < 2; i++) {
callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());
// The output is 0 and then 1, as expected.
// In contrast, the example would print 2 and then 2 in JavaScript.
在 switch-case 中使用 continue
和标签
var command = 'CLOSED';
switch (command) {
case 'CLOSED':
executeClosed();
continue nowClosed;
// Continues executing at the nowClosed label.
nowClosed:
case 'NOW_CLOSED':
// Runs for both CLOSED and NOW_CLOSED.
executeNowClosed();
break;
}
在开发过程中,使用 assert(condition, optionalMessage);
在 condition 为 false
时抛出异常、阻断程序继续执行。生产环境中忽略断言。
assert
起作用的情况:
- Flutter enables assertions in debug mode.
- Development-only tools such as dartdevc typically enable assertions by default.
- Some tools, such as dart and dart2js, support assertions through a command-line flag:
--enable-asserts
.
Functions
Dart 是完全面向对象的语言,甚至连函数都是对象、类型为 Function
,这意味着函数可以赋值给变量或者作为参数传递给其他函数,{ return expr; }
可简写为 => expr;
。
参数类型
- 必需的
- 可选的
- 命名的
- 位置的
首先列出必需参数,随后列出可选参数;可选参数可以是命名参数或者位置参数,但是不能同时存在。
void enableFlags({bool bold, bool hidden}) {...}
enableFlags(bold: true, hidden: false);
const Scrollbar({Key key, @required Widget child})
使用 @required
表示该参数强制需要,如果调用时没有提供,分析器会报问题,依赖 meta 包(import package:meta/meta.dart
)。
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') == 'Bob says Howdy');
assert(say('Bob', 'Howdy', 'smoke signal') ==
'Bob says Howdy with a smoke signal');
使用 =
为可选的命名或位置参数设置默认值(必须是编译时常量),如果不提供则默认为 null
。
void enableFlags({bool bold = false, bool hidden = 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');
返回值
所有函数都有返回值,如果没有指定返回值,return null
将会被隐式地追加到函数体。
foo() {}
assert(foo() == null);
main()
每个应用必须有一个顶级/全局的 main 函数,以作为整个应用的入口点。返回 void
,参数为一个可选的 List<String>
。
as first-class objects
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
// Pass printElement as a parameter.
list.forEach(printElement);
// Assign a (anonymous) function to a variable
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
Anonymous
没有名字的函数被称为匿名函数或 lambda 或闭包。()
之间零或多个参数,类型声明可选,逗号分隔;跟随 {}
,其中代码块即为函数体。
([[Type] param1[, …]]) {
codeBlock;
};
// If the function contains only one statement, shorten it using arrow notation
([[Type] param1[, …]]) => statement
scope
变量的作用范围仅仅通过代码布局即可静态得知:
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);
}
}
}
closures
闭包 是函数对象,可以访问语法范围内的变量,即使这个函数在定义它的范围之外去使用:
/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(num addBy) {
return (num i) => addBy + i;
}
void 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);
}
Classes
use classes
using constructors
new
关键字是可选的- 构造器名称可以是
ClassName
或者ClassName.identifier
- 常量构造器:
- 用来创建编译时常量
- 构造器前加
const
关键字 - 构造两个内容相同的编译时常量只会产生一个对象
- 在++常量上下文++中,可以在构造器前省去
const
关键字
using members
- 使用
.
来访问实例变量和方法 - 使用
?.
在对象为null
时来避免异常
get an object’s type
在运行时获取一个对象的类型,使用其 runtimeType
属性(继承自 Object
,为 Type
对象/实例)。
implement classes
constructors
class Point {
num x, y;
Point(num x, num y) {
// There's a better way to do this, stay tuned.
this.x = x;
this.y = y;
}
}
将构造器参数赋值给实例变量的操作十分常用,语法糖使该操作变得更加简单:
class Point {
num x, y;
// Syntactic sugar for setting x and y
// before the constructor body runs.
Point(this.x, this.y);
}
named constructors
- 为一个类实现多个构造器
- 使代码表意更加清晰
class Point {
num x, y;
Point(this.x, this.y);
// Named constructor
Point.origin() {
x = 0;
y = 0;
}
}
initializer list
初始化器的右侧(如
json['x']
)不能访问实例成员
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, num> json)
: x = json['x'],
y = json['y'] {
print('In Point.fromJson(): ($x, $y)');
}
开发阶段,在初始化列表中使用 assert
来验证输入参数:
Point.withAssert(this.x, this.y) : assert(x >= 0) {
print('In Point.withAssert(): ($x, $y)');
}
invoking a superclass 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');
}
}
传递给父类构造器的参数可以是一个表达式,例如一个方法调用,但是,不能是实例(this)相关、可以是类(static)相关的
class Employee extends Person {
Employee() : super.fromJson(defaultData);
// ···
}
在调用父类构造器的同时使用到初始化列表的话,初始化列表要位于父类构造器调用之前,具体执行顺序如下:
- 初始化列表
- 父类构造器
- 子类构造器体
redirecting constructors
有些构造器只是重定向到其他构造器:
class Point {
num x, 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
定义 const
构造器并确保所有实例变量为 final
,可以创建能够产生编译时常量对象的类。
class ImmutablePoint {
final num x, y;
const ImmutablePoint(this.x, this.y);
}
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) {
return _cache.putIfAbsent(
name, () => Logger._internal(name));
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
instance variables and methods
- 如果实例变量在被定义之时就初始化,那么,这个设置的执行将早于初始化列表和构造器体
- 实例变量默认具有getter和setter(非
final
)方法,使用get
、set
关键字可以另外创建属性/实例变量
implicit interfaces
任何一个类都++隐式地定义了++一个接口,这个接口包含了对应的类以及该类所实现接口的所有实例成员。如果类 A 想要支持类 B 的接口而不想继承它的实现,那么,类 A 可以实现类 B 对应的接口。
// 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(String who) => 'Hello, $who. I am $_name.';
}
// An implementation of the Person interface.
class Impostor implements Person {
get _name => '';
String greet(String who) => 'Hi $who. Do you know who I am?';
}
String greetBob(Person person) => person.greet('Bob');
void main() {
print(greetBob(Person('Kathy')));
print(greetBob(Impostor()));
}
// A class implements multiple interfaces
class Point implements Comparable, Location {...}
noSuchMethod()
重写 noSuchMethod()
来监测或响应客户代码试图调用该类尚不存在的实例成员:
class A {
// Unless you override noSuchMethod, using a
// non-existent member results in a NoSuchMethodError.
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
只有满足如下条件才能调用尚不存在的实例成员:
- 将该类的实例赋值给
dynamic
变量 - 该类已经实现
noSuchMethod()
且存在抽象方法
void main() {
dynamic f1 = Foo1();
f1.method1();
print(f1.v1);
var f2 = Foo2();
f2.method2();
}
class Foo1 {
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
class Foo2 {
void method2();
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
mixins
在多类层次结构中复用一个类的代码,使用 with
关键字、在其后添加 mixin 名称。
class Musician extends Performer with Musical {
// ···
}
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
只继承 Object
且未声明构造器的类即可作为 mixin,如果不想使用常规类,可以将关键字 class
替换为 mixin
。
mixin 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');
}
}
}
使用 on
关键字指定在使用该 mixin 前必须集成的父类。
mixin MusicalPerformer on Musician {
// ···
}
Libraries and visibility
Libraries 不仅提供 APIs,还是隐私单元,以 _
开头的标识符只在当前 Library 中可见。
- library
- visibility
- package
- https://dart.dev/guides/packages
Generics
集合字面量可以被参数化:
var names = <String>['Seth', 'Kathy', 'Lars'];
var uniqueNames = <String>{'Seth', 'Kathy', 'Lars'};
var pages = <String, String>{
'index.html': 'Homepage',
'robots.txt': 'Hints for web robots',
'humans.txt': 'We are people, not machines'
};
泛型类型规范化,在运行时携带其类型信息:
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
Exceptions
异常表示发生意外事情,如果异常没有被捕获,所抛出异常的 isolate
会被暂停,并且通常其对应的程序也会被终止。
Dart 提供 Exception
、Error
类型,以及众多子类,也可以自定义异常类型,甚至抛出异常非空对象作为异常。
Throw
// an example of throwing, or raising, an exception
throw FormatException('Expected at least 1 section');
// throw arbitrary objects
throw 'Out of llamas!';
生产质量的代码通常抛出实现
Exception
或Error
的异常类型对象
Catch
需要异常类型时使用 on
关键字,需要异常对象时使用 catch
关键字:
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');
}
可以为 catch
制定一个或两个参数,第一个是所抛出的异常对象,第二个是 StackTrace
对象:
try {
// ···
} on Exception catch (e) {
print('Exception details:\n $e');
} catch (e, s) {
print('Exception details:\n $e');
print('Stack trace:\n $s');
}
如果只是部分处理一个异常,然后让它继续传递,则使用 rethrow
关键字:
void misbehave() {
try {
dynamic foo = true;
print(foo++); // Runtime error
} 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}.');
}
}
Asynchrony
异步函数/方法:设置完可能耗时操作(如 I/O)后,不等待其完成、直接返回 Future
(异步操作终将返回处理结果的一种承诺)或 Stream
(获取数据/事件序列的一种方式)对象。
声明
在函数/方法上添加 async
关键字,使之返回 Future
对象:
// String lookUpVersion() => '1.0.0';
Future<String> lookUpVersion() async => '1.0.0';
函数/方法体内不需要使用 Future
API,Dart 会自动为其按需创建 Future
对象;如果函数/方法没有返回值,则其返回值类型声明为 Future<void>
。
处理 Future
Future checkVersion() async {
var version = await lookUpVersion();
// Do something with version
}
async
函数/方法在执行到其中的第一个 await
表达式时直接返回 Future
对象,然后在 await
表达式处理完成时(异步操作返回处理结果值)继续执行函数/方法剩余部分。
var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
在 async
函数/方法中,可以使用 await
多次。
确保
await
在async
函数/方法体内
处理 Stream
await for (varOrType identifier in expression) {
// Executes each time the stream emits a value.
}
expression 为 Stream
类型,执行流程:
- 等待着 stream 发射出一个值
- 使用该值执行一遍循环体
- 重复 1、2 步骤,直到 stream 关闭
使用 break
或 return
可以打断 for
循环的执行,同时取消对当前 stream 的订阅、停止对其监听。
使用
await for
时,要确保这样会使代码更清晰,以及,想要等待 stream 的所有值
Generators
使用 generator function 实现延迟(懒惰)生产序列值。
synchronous generator function
标记函数体为 sync*
,并使用 yield
传递值,返回 Iterable
对象:
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
asynchronous generator function
标记函数体为 sync*
,并使用 yield
传递值,返回 Stream
对象:
Stream<int> asynchronousNaturalsTo(int n) async* {
int k = 0;
while (k < n) yield k++;
}
Metadata
提供关于代码的额外信息,以 @
符号开头,后面紧跟一个运行时常量的引用或者常量构造器的调用。@deprecated
和 @override
在所有 Dart 代码中都可用。
自定义实现:
library todo;
class Todo {
final String who;
final String what;
const Todo(this.who, this.what);
}
使用示例:
import 'todo.dart';
@Todo('seth', 'make this do something')
void doSomething() {
print('do something');
}
Typedefs
函数也是对象,在作为变量或返回值时会丢失其类型信息,使用 typedef
(也叫函数类型别名)给函数类型起一个名字来避免这种情况。
typedef Compare = int Function(Object a, Object b);
//typedef Compare<T> = int Function(T a, T b);
class SortedCollection {
// Function compare;
Compare compare;
// SortedCollection(int f(Object a, Object b)) {
// compare = f;
// }
SortedCollection(this.compare);
}
int sort(Object a, Object b) => 0;
void main() {
SortedCollection coll = SortedCollection(sort);
assert(coll.compare is Function);
assert(coll.compare is Compare);
}
Callable classes
在类中实现 call()
后,就可以像调用函数一样使用其实例/对象:
class WannabeFunction {
call(String a, String b, String c) => '$a $b $c!';
}
main() {
var wf = WannabeFunction();
var out = wf("Hi","there,","gang");
print('$out');
}
Isolates
为了利用好多核 CPU,通常会使用共享内存的多线程来同时执行,这种共享状态的并发容易产生错误且导致代码复杂。Dart 使用 isolates
取代线程,每个 isolate 都拥有它自己的内存堆,以确保其中的状态信息不被其他 isolate 所访问。
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) {
// ...
}
}
在注释中,使用 []
指向其他元素(类、方法、属性、全局变量、函数或参数)。