这篇文章将会向你展示如何使用Dart的每一个知识点,包括变量,操作符,类,类库,但这是在你已经有一个其他语言的编码经验的前提下。
为了学习Dart更多关于Dart的核心类库,请查看A Tour of the Dart Libraries,当你想知道更多语言特征,挺查阅Dart language specification
你可以通过DartPad 来尝试运行Dart代码:Open DartPad
一个基本的Dart程序
// 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.
}
以下是此程序使用的适用于所有(或几乎所有)Dart应用程序的内容:
// This is a comment.
单行注释。Dart还支持多行和文档注释。有关详情,请参阅注释
int
整数类型。一些其他的内置类型 是String
,List
和bool
。
42
一个整数字面值,这是一个编译时常量。
print()
一个方便的输出函数。
'...' (or "...")
字符串字面值。
$variableName(或)${expression}
字符串插值:包括字符串字面值内部的变量或表达式的字符串。有关更多信息,请参阅 字符串。
main()
一个特殊的顶级(top)的必须的(requared)app应用的入口函数,有关更多信息,请参阅 main()函数。
var
一种声明变量而不指定其类型的方法。
重要的概念
当您了解Dart语言时,请记住以下事实和概念:
- 变量所指向的所有值都是对象,每个对象都是一个类的实例。数字,函数(函数也是一个变量)和
null
对象等。所有对象都继承自Object类。 - 尽管Dart是强类型的,但类型声明是可选的,因为Dart可以推断类型。在上面的代码中,
number
推断为类型int
。如果要明确说明不需要任何类型,请 使用特殊类型dynamic
。 - Dart支持泛型类型,如List<int>(整数List)或List<dynamic>(任何类型的对象List)。
- Dart支持顶级函数(例如main()),以及绑定到类或对象的函数(分别是静态和实例方法)。您还可以在函数内创建函数(嵌套函数或本地函数)。
- 类似地,Dart支持顶级变量,以及绑定到类或对象的变量(静态和实例变量)。实例变量有时称为字段或属性。
- 与Java,Dart不具备关键字
public
,protected
和private
。如果标识符以下划线(_)开头,则它对其库是私有的。有关详细信息,请参阅 库和可见性。 - 标识符可以以字母或下划线(_)开头,后跟这些字符加数字的任意组合。
- Dart可以使用表达式(具有运行时值)和 语句(不具有)。例如,条件表达式
condition ? expr1 : expr2
的值为expr1
或expr2
。将其与if-else语句进行比较,语句没有任何值。语句通常包含一个或多个表达式,但表达式不能直接包含语句。 - Dart工具可以报告两种问题:警告和错误。警告只是表明您的代码可能无法正常工作,但它们不会阻止您的程序执行。错误可以是编译时或运行时。编译时错误会阻止代码执行; 运行时错误导致 代码执行时引发异常。
关键字
下表列出了Dart语言的关键字:
abstract 2 | dynamic 2 | implements 2 | show 1 |
---|---|---|---|
as 2 | else | import 2 | static 2 |
assert | enum | in | super |
async 1 | export 2 | interface 2 | switch |
await 3 | extends | is | sync 1 |
break | external 2 | library 2 | this |
case | factory 2 | mixin 2 | throw |
catch | false | new | true |
class | final | null | try |
const | finally | on 1 | typedef 2 |
continue | for | operator 2 | var |
covariant 2 | Function 2 | part 2 | void |
default | get 2 | rethrow | while |
deferred 2 | hide 1 | return | with |
do | if | set 2 | yield 3 |
避免使用这些单词作为标识符。但是,如有必要,标有上标的关键字可以是标识符:
- 带有上标1的单词是上下文关键字,仅在特定位置具有含义。
- 带有上标2的单词是内置标识符。为了简化将JavaScript代码移植到Dart的任务,这些关键字在大多数地方都是有效的标识符,但它们不能用作类或类型名称,也不能用作导入前缀。
- 带有上标3的单词是与Dart 1.0发布后添加的异步支持相关的更新,有限的保留字。不能使用
await
或yield
作为任何函数体中的标识符标记async
,async*
或sync*
。
表中的所有其他单词都是保留字,不能是标识符。
变量
这是创建变量并初始化它的示例:
var name = 'Bob' ;
变量存储引用。名为name的变量指向String值为“Bob” 的对象。
name
推断变量的类型String
,但您可以通过指定它来更改该类型。如果对象不限于单一类型,请按照设计准则指定Object
或dynamic
类型 。
dynamic name = 'Bob' ;
另一种选择是显式声明类型:
String name = 'Bob' ;
注意: 此页面遵循 风格指南建议 使用
var
,而不是指定类型,用于局部变量。
默认值
未初始化的变量的初始值为null。即使是具有数字类型的变量最初也是null,因为数字 - 就像Dart中的其他所有都是对象。
int lineCount ;
assert (lineCount == null );
注意:生产环境代码中将忽略assert()
调用。在开发期间, 除非条件为真,否则抛出异常。有关详细信息,请参阅断言。
Final and const
如果您从未打算更改变量,请使用final或const代替var或替代类型。最终变量只能设置一次; const变量是编译时常量。(Const变量是隐式final的。)final的顶级或类变量在第一次使用时被初始化。
以下是创建和设置最终变量的示例:
final name = 'Bob'; // Without a type annotation
final String nickname = 'Bobby';
您无法更改最终变量的值:
name = 'Alice' ; //错误:最终变量只能设置一次。
使用const为您要为变量的编译时间常数。如果const变量在类级别,请声明为static const。在声明变量的地方,将值设置为编译时常量,例如数字或字符串字面值,const变量或对常数进行算术运算的结果:
const bar = 1000000; // 压力单位 (dynes/cm2)
const double atm = 1.01325 * bar; // 标准大气压
该const关键字不只是声明常数变量。您还可以使用它来创建常量值,以及声明创建常量值的构造函数。任何变量都可以具有常量值。
var foo = const [];
final bar = const [];
const baz = []; //相当于`const []`
您可以省略声明const
的初始化表达式,如上所述。有关详细信息,请参阅不要冗余地使用const。const``baz
您可以更改非final,非const变量的值,即使它曾经有一个const值:
foo = [1, 2, 3]; // Was const []
您无法更改const变量的值:
baz = [ 42 ]; //错误:无法为常量变量赋值。
有关使用const
创建常量值的更多信息,请参阅 Lists,Maps和Classes。
内置类型
Dart语言特别支持以下类型:
- numbers
- strings
- booleans
- lists (also known as arrays)
- sets
- maps
- runes (for expressing Unicode characters in a string)
- symbols
您可以使用字面值初始化任何这些特殊类型的对象。例如,'this is a string'是一个字符串字面值,true是一个布尔字面值。
因为Dart中的每个变量都引用一个对象 - 一个类的实例 - 您通常可以使用构造函数来初始化变量。一些内置类型有自己的构造函数。例如,您可以使用Map()构造函数来创建Map。
Numbers
Dart 数字有两种形式:
int
整数值不大于64位,具体取决于平台。在Dart VM上,值可以是-2 63到2 63 - 1.编译为JavaScript的Dart使用 JavaScript编号, 允许从-2 53到2 53 - 1的值。
double
64位(双精度)浮点数,由IEEE 754标准规定。
这两个int
和double
的亚型num
。 num类型支持基本的运算符,如+, - ,/和*,以及abs()
,ceil()
和floor()
其他方法已在其中定义。(按位运算符,例如>>,在int
类中定义。)如果num及其子类型没有您要查找的内容,则 dart:math库可能会定义。
整数是没有小数点的数字。以下是定义整型字面值的一些示例:
var x = 1;
var hex = 0xDEADBEEF;
如果数字包含小数,则为双精度数。以下是定义双精度字面值的一些示例:
var y = 1.1;
var exponents = 1.42e5;
从Dart 2.1开始,必要时整型字面值会自动转换为双精度数:
double z = 1; // Equivalent to double z = 1.0.
版本说明: 在Dart 2.1之前,在双精度数变量赋值为整型字面值是错误的。
以下是将字符串转换为数字的方法,反之亦然:
// 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');
int类型具有传统的按位移位(<<,>>),AND(&)和OR(|)运算符。例如:
assert((3 << 1) == 6); // 0011 << 1 == 0110
assert((3 >> 1) == 1); // 0011 >> 1 == 0001
assert((3 | 4) == 7); // 0011 | 0100 == 0111
字面值数字是编译时常量。许多算术表达式也是编译时常量,因为它们的操作数是编译为数字的编译时常量。
const msPerSecond = 1000;
const secondsUntilRetry = 5;
const msUntilRetry = secondsUntilRetry * msPerSecond;
Strings
Dart字符串是一系列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.";
您可以使用表达式将表达式的值放在字符串中 。如果表达式是标识符,则可以跳过{}。要获取与对象相对应的字符串,Dart会调用该对象的方法。${expression}toString()
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!');
注: 在==两个物体操作测试是否是等价的。如果两个字符串包含相同的代码单元序列,则它们是等效的。(这一点和java是不同的,java,String的==运算符,只有当地址一样,字面值一样,才能使相等的,dart String 的== 相当于java String的equal)
您可以使用相邻的字符串字面值或+ 运算符来连接字符串:
var s1 = 'String '
'concatenation'
" works even over line breaks.";
assert(s1 ==
'String concatenation works even over '
'line breaks.');
var s2 = 'The + operator ' + 'works, as well.';
assert(s2 == 'The + operator works, as well.');
创建多行字符串的另一种方法:使用带有单引号或双引号的三引号:
var s1 = '''
You can create
multi-line strings like this one.
''';
var s2 = """This is also a
multi-line string.""";
您可以通过为其添加前缀来创建“raw”字符串r:
var s = r'In a raw string, not even \n gets special treatment.';
有关如何在字符串中表示Unicode字符的详细信息,请参阅Runes。
字符串字面值是编译时常量,因为任何插值表达式是一个编译时常量,其值为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
为了表示布尔值,Dart具有一个名为的类型bool。只有两个对象具有bool类型:boolean 字面值 true和false,它们都是编译时常量。
Dart的类型安全意味着你不能使用像或 那样的代码,如下:
if (nonbooleanValue)
assert (nonbooleanValue)
应该明确能计算出值:
// Check for an empty string.
var fullName = '';
assert(fullName.isEmpty);
// Check for zero.
var hitPoints = 0;
assert(hitPoints <= 0);
// Check for null.
var unicorn;
assert(unicorn == null);
// Check for NaN.
var iMeantToDoThis = 0 / 0;
assert(iMeantToDoThis.isNaN);
Lists
也许几乎每种编程语言中最常见的集合是数组或有序的对象组。在Dart中,数组是 List对象,因此大多数人只是将它们称为列表。
Dart List文字看起来像JavaScript数组字面值。这是一个简单的Dart List:
var list = [1, 2, 3];
注意: Dart推断出
list
有类型List<int>
。如果尝试将非整数对象添加到此List,则分析器或运行时会引发错误。有关更多信息,请阅读 类型推断。
List使用从零开始的索引,list.length - 1是最后一个元素的索引。您可以获得List的长度并像在JavaScript中一样引用List元素:
var list = [1, 2, 3];
assert(list.length == 3);
assert(list[1] == 2);
list[1] = 1;
assert(list[1] == 1);
要创建一个编译时常量const的List,请在List文字前添加const:
var constantList = const [1, 2, 3];
// constantList[1] = 1; // Uncommenting this causes an error.
List类型有许多方便的方法来操作List。有关List的更多信息,请参阅泛型和 集合。
Sets
Dart中的set是一组无序的独特item集合。对集合的Dart支持由set 字面值和Set类型提供。
这是一个简单的Dart集,使用set 字面值创建:
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
Dart推断出
halogens
具有该类型Set<String>
。如果您尝试向集合中添加错误类型的值,则分析器或运行时会引发错误。有关更多信息,请阅读 类型推断。
要创建一个空集,请使用{}前面带有类型参数,或者指定{}给类型的变量Set:
var names = <String>{};
// Set<String> names = {}; // This works, too.
// var names = {}; // Creates a map, not a set.
Set还是Map? Map字面值的语法类似于Set字面值的语法。由于Map的优先级靠前,因此{}默认为Map类型。如果您忘记了类型注释{}或它所分配的变量,则Dart会创建一个类型的对象Map<dynamic, dynamic>。
使用add()或addAll()方法将项添加到现有集:
var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);
使用.length得到的一组Set的数量:
var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);
assert(elements.length == 5);
要创建一个编译时常量const的集合,请在set literal之前添加const
:
final constantSet = const {
'fluorine',
'chlorine',
'bromine',
'iodine',
'astatine',
};
// constantSet.add('helium'); // Uncommenting this causes an error.
Maps
通常,映射是关联键和值的对象。键和值都可以是任何类型的对象。每个键只出现一次,但您可以多次使用相同的值。Map的Dart支持由Map字面值和Map类型提供。
这里有几个简单的Dart Map例子,使用Map字面值创建:
var gifts = {
// Key: Value
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings'
};
var nobleGases = {
2: 'helium',
10: 'neon',
18: 'argon',
};
Dart推断出
gifts
的类型Map<String, String>以及nobleGases
类型的 DartMap<int, String>
。如果您尝试将错误类型的值添加到任一映射,则分析器或运行时会引发错误。有关更多信息,请阅读 类型推断。
您可以使用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';
注意: 您可能想添加
new
而不仅仅是Map()
。从Dart 2开始,new
关键字是可选的。有关详细信息,请参阅使用构造函数。
像在JavaScript中一样,将新的键值对添加到现有Map中:
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds'; // Add a key-value pair
以与在JavaScript中相同的方式从Map中检索值:
var gifts = {'first': 'partridge'};
assert(gifts['first'] == 'partridge');
如果您查找不在Map中的键,则会得到null作为回报:
var gifts = {'first': 'partridge'};
assert(gifts['fifth'] == null);
使用.length得到的映射中的键值对的数量:
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds';
assert(gifts.length == 2);
要创建一个编译时常量const的Map,请在Map字面值之前添加const
:
有关Map的更多信息,请参阅 泛型和 Map。
Runes
在Dart中,Runes是字符串的UTF-32表达。
Unicode为世界上所有书写系统中使用的每个字母,数字和符号定义唯一的数值。由于Dart字符串是UTF-16代码单元的序列,因此在字符串中表示32位Unicode值需要特殊语法。
表达Unicode代码点的常用方法是 \uXXXX,XXXX是4位十六进制值。例如,心脏角色(♥)是\u2665。要指定多于或少于4个十六进制数字,请将值放在大括号中。例如,笑的表情符号(😆)是\u{1f600}。
该字符串 类有几个属性,你可以用它来提取Runes信息。在codeUnitAt
和codeUnit
属性返回16位编码单元。使用该runes
属性获取字符串的Runes。
以下示例说明了符文,16位代码单元和32位代码点之间的关系。
main() {
var clapping = '\u{1f44f}';
print(clapping);
print(clapping.codeUnits);
print(clapping.runes.toList());
Runes input = new Runes(
'\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}');
print(new String.fromCharCodes(input));
}
注意: 使用列表操作操作符文时要小心。这种方法很容易使字符串解体,具体取决于特定的语言,字符集和操作。有关更多信息,请参阅 如何在Dart中反转字符串?在Stack Overflow上。
Symbols
Symbol对象表示在Dart程序中声明的运算符或标识符。您可能永远不需要使用符号,但它们对于按名称引用标识符的API非常有用,因为缩小会更改标识符名称而不会更改标识符符号。要获取标识符的符号,请使用符号文字,它只是#后跟标识符:(这一段实在不会翻译了- -
)
要获取标识符的符号,请使用符号文字, #
后面跟着标识符:
#radix
#bar
Symbols文字是编译时常量。
Functions
Dart是一种真正的面向对象语言,因此即使是函数也是对象并且具有类型Function。 这意味着函数可以分配给变量或作为参数传递给其他函数。您也可以调用Dart类的实例,就好像它是一个函数一样。有关详细信息,请参阅Callable classes
以下是实现函数的示例:
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
虽然Effective Dart建议 为公共API声明返回值类型,但如果省略类型,该函数仍然有效:
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
对于只包含一个表达式的函数,可以使用简写语法:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
这个 => 是{ return expr; }的速记 . 。有时将=> 表示法称为箭头语法。
只有表达式(而不是语句)能够出现在箭头(=>)和分号(;)之间。例如,您不能在其中放置if语句,但可以使用条件表达式。
函数可以有两种类型的参数:必需和可选。首先列出所需参数,然后列出任何可选参数。命名的可选参数也可以标记为@required
。有关详细信息,请参阅下一节。
可选参数
可选参数可以是位置参数,也可以是名称参数,但不能同时包含
可选的名称参数
调用函数时,可以使用指定名称参数 。例如:paramName: value
enableFlags (bold :true ,hidden :false );
定义函数时,用于 指定名称参数:{param1, param2, …}
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold, bool hidden}) {...}
Flutter实例创建表达式可能变得复杂,因此窗口小部件构造函数仅使用命名参数。这使得实例创建表达式更易于阅读。
您可以使用@required在任何Dart代码(不仅仅是Flutter)中注释命名参数, 以指示它是必需参数。例如:
const Scrollbar({Key key, @required Widget child})
当一个Scrollbar构造,当必要参数缺少时,编译器会报错。
requared在mate包中定义。可以package:meta/meta.dart
直接导入 ,也可以导入另一个导出的包 meta
,例如Flutter package:flutter/material.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。
以下是为命名参数设置默认值的示例:
/// Sets the [bold] and [hidden] flags ...
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');
您还可以将List或Map作为默认值传递。以下示例定义了一个函数,该函数doStuff()指定参数的默认List和list 参数的默认Map gifts。
void doStuff(
{List<int> list = const [1, 2, 3],
Map<String, String> gifts = const {
'first': 'paper',
'second': 'cotton',
'third': 'leather'
}}) {
print('list: $list');
print('gifts: $gifts');
}
main()函数
每个应用程序都必须具有顶级main()功能,该功能用作应用程序的入口点。该main()函数返回void并具有List<String>参数的可选参数。
以下main()是Web应用程序功能的示例:
void main() {
querySelector('#sample_text_id')
..text = 'Click me!'
..onClick.listen(reverseText);
}
注意:
..
前面代码中 的语法称为级联。使用级联,您可以对单个对象的成员执行多个操作。
以下main()是带参数的命令行应用程序的函数示例:
// Run the app like this: dart args.dart 1 test
void main(List<String> arguments) {
print(arguments);
assert(arguments.length == 2);
assert(int.parse(arguments[0]) == 1);
assert(arguments[1] == 'test');
}
您可以使用args库来定义和解析命令行参数。
Function 作为第一类对象的功能
您可以将函数作为参数传递给另一个函数。例如:
void printElement(int 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 !!!');
此示例使用了匿名函数。更多关于下一节的内容。
匿名函数
大多数函数都被命名,例如main()或printElement()。您还可以创建一个名为匿名函数的无名函数,有时也可以创建一个lambda或闭包。您可以为变量分配匿名函数,以便例如可以在集合中添加或删除它。
匿名函数看起来类似于命名函数 - 零个或多个参数,在逗号和括号之间用逗号和可选类型注释分隔。
后面的代码块包含函数的主体:
([[Type] param1[, …]]) {
codeBlock;
};
以下示例使用无类型参数item定义匿名函数,为List中的每个项调用的函数将打印一个包含指定索引处的值的字符串。
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
print('${list.indexOf(item)}: $item');
});
如果函数只包含一个语句,则可以使用箭头表示法缩短它。
list.forEach(
(item) => print('${list.indexOf(item)}: $item'));
作用域
Dart是有作用域的语言,这意味着变量的范围是静态确定的,只需通过代码的布局。您可以“向外跟随花括号”以查看变量是否在范围内。
以下是每个范围级别包含变量的嵌套函数示例:
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);
}
}
}
请注意如何使用nestedFunction()每个级别的变量,一直到顶级。
闭包
一个闭包是能够访问在其作用域的变量的函数的对象,即使当函数用于其原来的范围之外。
函数可以关闭周围范围中定义的变量。在以下示例中,makeAdder()捕获变量addBy。无论返回的函数在哪里,它都记录addBy。
/// 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);
}
测试函数是否相等
以下是测试顶级函数,静态方法和实例方法的相等性的示例:
void foo() {} // A top-level function
class A {
static void bar() {} // A static method
void baz() {} // An instance method
}
void main() {
var x;
// Comparing top-level functions.
x = foo;
assert(foo == x);
// Comparing static methods.
x = A.bar;
assert(A.bar == x);
// Comparing instance methods.
var v = A(); // Instance #1 of A
var w = A(); // Instance #2 of A
var y = w;
x = w.baz;
// These closures refer to the same instance (#2),
// so they're equal.
assert(y.baz == x);
// These closures refer to different instances,
// so they're unequal.
assert(v.baz != w.baz);
}
返回值
foo() {}
assert(foo() == null);
运算符
Dart定义下表中显示的运算符。您可以覆盖许多运算符,如在 可重写运算符。
描述 | 运算符 |
---|---|
一元后缀 | expr++ expr-- () [] . ?. |
一元前缀 | -expr !expr ~expr ++expr --expr |
乘 | * / % ~/ |
添加 | + - |
位移 | << >> >>> |
按位与 | & |
按位异或 | ^ |
按位或 | | |
大小和类型测试 | >= > <= < as is is! |
相等性 | == != |
逻辑与 | && |
逻辑或 | || |
非空 | ?? |
?表达式 | expr1 ? expr2 : expr3 |
级联 | .. |
再运算 | = *= /= += -= &= ^= etc. |
警告: 运算符优先级是Dart解析器行为。有关明确的规则,请参阅Dart语言规范中的语法
使用运算符时,可以创建表达式。以下是运算符表达式的一些示例:
a++
a + b
a = b
a == b
c ? a : b
a is T
在运算符表中,每个运算符的优先级高于其后的行中的运算符。例如,乘法运算符的%
优先级高于(因此之前执行)等于运算符==
,它的优先级高于逻辑AND运算符&&
。该优先级意味着以下两行代码执行相同的方式:
// Parentheses improve readability.
if ((n % i == 0) && (d % i == 0)) ...
// Harder to read, but equivalent.
if (n % i == 0 && d % i == 0) ...
警告: 对于处理两个操作数的运算符,最左边的操作数确定使用哪个对象的运算符。例如,如果您有Vector对象和Point对象,则aVector + aPoint使用Vector对象的+。
算术运算符
Dart支持通常的算术运算符,如下表所示。
Operator | Meaning |
---|---|
+ | 加 |
– | 减 |
-expr | 负号 |
* | 乘法 |
/ | 除法 |
~/ | 整除 |
% | 获取整数除法的余数(模数) |
例:
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 int
assert(5 % 2 == 1); // Remainder
assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');
Dart还支持前缀和后缀增量和减量运算符。
操作者 | 含义 |
---|---|
++var | var = var + 1(表达式值是var + 1) |
var++ | var = var + 1(表达式值是var) |
--var | var = var – 1(表达式值是var – 1) |
var-- | var = var – 1(表达式值是var) |
var a, b;
a = 0;
b = ++a; // Increment a before b gets its value.
assert(a == b); // 1 == 1
a = 0;
b = a++; // Increment a AFTER b gets its value.
assert(a != b); // 1 != 0
a = 0;
b = --a; // Decrement a before b gets its value.
assert(a == b); // -1 == -1
a = 0;
b = a--; // Decrement a AFTER b gets its value.
assert(a != b); // -1 != 0
相等和关系运算符
下表列出了相等运算符和关系运算符的含义。
操作者 | 含义 |
---|---|
== | 等于; 见下面的讨论 |
!= | 不相等 |
> | 比...更棒 |
< | 少于 |
>= | 大于或等于 |
<= | 小于或等于 |
要测试两个对象x和y是否表示相同的事物,请使用 ==
运算符。(在极少数情况下,您需要知道两个对象是否是完全相同的对象,请使用相同的() 函数。)以下是==
运算符的工作方式:
如果x或y为null,则如果两者都为null则返回true;如果只有一个为null,则返回false。
返回方法调用的结果 。(这是正确的,运算符,例如在第一个操作数上调用的方法。您甚至可以覆盖许多运算符,包括,正如您在Overridable运算符中看到的那样 。)
*x*.==(*y*)``==``==
这是使用每个相等和关系运算符的示例:
assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);
类型检测运算符
使用as,is和is!运算符可以方便地在运行时检查类型。
操作者 | 含义 |
---|---|
as | Typecast(也用于指定库前缀) |
is | 如果对象具有指定的类型,则为True |
is! | 如果对象具有指定的类型,则返回false |
obj is T如果obj实现了指定的接口,则结果为true T。例如,obj is Object总是如此。
使用as运算符将对象强制转换为特定类型。通常,您应该使用它作为is对使用该对象的表达式后跟对象的测试的简写。例如,请考虑以下代码:
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
您可以使用as运算符缩短代码:
(emp as Person).firstName = 'Bob';
注意: 代码不相同。如果emp为null或不是Person,则第一个示例(with is)不执行任何操作; 第二个(带as)抛出一个异常。
赋值操作符
如您所见,您可以使用=运算符分配值。要仅在变量为null时分配,请使用??=运算符,如下,如果为null就执行f
// Assign value to a
a = value;
// Assign value to b if b is null; otherwise, b stays the same
b ??= value;
复合赋值运算符,例如+=将操作与赋值组合在一起。
= | –= | /= | %= | >>= | ^= |
---|---|---|---|---|---|
+= | *= | ~/= | <<= | &= | |= |
以下是复合赋值运算符的工作原理:
复合赋值 | 等价表达 | |
---|---|---|
对于运营商op: | a op= b | a = a op b |
例: | a += b | a = a + b |
以下示例使用赋值和复合赋值运算符:
var a = 2; // Assign using =
a *= 3; // Assign and multiply: a = a * 3
assert(a == 6);
逻辑运算符
您可以使用逻辑运算符反转或组合布尔表达式
操作者 | 含义 |
---|---|
!expr | 反转以下表达式(将false更改为true,反之亦然) |
|| | 逻辑或 |
&& | 逻辑AND |
以下是使用逻辑运算符的示例:
if (!done && (col == 0 || col == 3)) {
// ...Do something...
}
按位和移位运算符
您可以在Dart中操纵数字的各个位。通常,您将使用这些按位和移位运算符和整数。
操作者 | 含义 |
---|---|
& | 和 |
| | 或 |
^ | 异或 |
~expr | 一元逐位补码(0s变为1s; 1s变为0s) |
<< | 向左位移 |
>> | 向右位移 |
这是使用按位和移位运算符的示例:
final value = 0x22;
final bitmask = 0x0f;
assert((value & bitmask) == 0x02); // AND
assert((value & ~bitmask) == 0x20); // AND NOT
assert((value | bitmask) == 0x2f); // OR
assert((value ^ bitmask) == 0x2d); // XOR
assert((value << 4) == 0x220); // Shift left
assert((value >> 4) == 0x02); // Shift right
条件表达式
Dart有两个运算符,可以让您简明地计算可能需要if-else语句的表达式:
condition ? expr1 : expr2
如果condition为true,则计算expr1(并返回其值); 否则,计算并返回expr2的值。
`expr1 ?? expr2`
如果expr1为非null,则返回其值; 否则,计算并返回expr2的值。
当您需要根据布尔表达式分配值时,请考虑使用?:
var visibility = isPublic ?'public' :'private' ;
如果布尔表达式测试为null,请考虑使用??。
String playerName(String name) => name ?? 'Guest';
前面的例子至少可以用其他两种方式编写,但不够简洁:
// Slightly longer version uses ?: operator.
String playerName(String name) => name != null ? name : 'Guest';
// Very long version uses if-else statement.
String playerName(String name) {
if (name != null) {
return name;
} else {
return 'Guest';
}
}
级联符号..
(Cascades)级联符号 .. 允许您对同一对象进行一系列操作。除了函数调用,您还可以访问同一对象上的字段。这通常可以为您节省创建临时变量的步骤,并允许您编写更多流畅的代码。
请考虑以下代码:
querySelector('#confirm') // Get an object.
..text = 'Confirm' // Use its members.
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
第一个方法调用,querySelector()返回一个选择器对象。级联表示法后面的代码对此选择器对象进行操作,忽略可能返回的任何后续值。
前面的例子相当于:
var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));
您也可以嵌套您的级联。例如:
final addressBook = (AddressBookBuilder()
..name = 'jenny'
..email = 'jenny@example.com'
..phone = (PhoneNumberBuilder()
..number = '415-555-0100'
..label = 'home')
.build())
.build();
小心在返回实际对象的函数上构造级联。例如,以下代码失败:
var sb = StringBuffer();
sb.write('foo')
..write('bar'); // Error: method 'write' isn't defined for 'void'.
//返回为void的不能再构造级联
.setTitle
.setView
.setIcon
该sb.write()调用无返回值,不能构造void的级联。
其他运算符
在其他示例中,您会看到了大多数剩余的运算符:
操作者 | 名称 | 含义 |
---|---|---|
() | 功能应用 | 表示函数调用 |
[] | 列表访问 | 引用列表中指定索引处的值 |
. | 成员访问 | 指表达式的属性; 示例:从表达式中foo.bar选择属性barfoo |
?. | 有条件的成员访问权限 | 比如.,但最左边的操作数可以为null; 示例:从表达式中foo?.bar选择属性bar,foo除非foo为null(在这种情况下,值为foo?.barnull) |
控制流程语句
您可以使用以下任一方法控制Dart代码的流程:
- if 和 else
- for 循环
- while和do- while循环
- break 和 continue
- switch 和 case
- assert
您还可以使用try-catch
和影响控制流throw
,如异常中所述。
If and else
Dart支持if
带有可选else
语句的语句,如下一个示例所示。另见条件表达式。
if (isRaining()) {
you.bringRainCoat();
} else if (isSnowing()) {
you.wearJacket();
} else {
car.putTopDown();
}
for循环
您可以使用标准for循环进行迭代。例如:
var message = StringBuffer('Dart is fun');
for (var i = 0; i < 5; i++) {
message.write('!');
}
Dart for循环内部的闭包捕获了索引的值,避免了JavaScript中常见的陷阱。例如,考虑:
var callbacks = [];
for (var i = 0; i < 2; i++) {
callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());
如预期一样,会输出0然后1。相反,在js里会打印出2个2。
如果要迭代的对象是Iterable,则可以使用 forEach()方法。forEach()
如果您不需要知道当前的迭代计数器,则使用是一个不错的选择:
candidates.forEach((candidate) => candidate.interview());
像List和Set这样for-in
的可迭代类也支持迭代的形式 :
var collection = [0, 1, 2];
for (var x in collection) {
print(x); // 0 1 2
}
While and do-while
一个while循环在执行循环内的逻辑之前,先计算表达式是否符合条件:
while (!isDone()) {
doSomething();
}
一个while循环先计算表达式是否符合条件,然后执行循环内的逻辑:
do {
printLine();
} while (!atEndOfPage());
Break and continue
使用break停止循环:
while (true) {
if (shutDownRequested()) break;
processIncomingRequests();
}
使用continue跳到下一个循环迭代:
for (int i = 0; i < candidates.length; i++) {
var candidate = candidates[i];
if (candidate.yearsExperience < 5) {
continue;
}
candidate.interview();
}
如果您使用Iterable(如列表或集合),则可能会以不同的方式编写该示例 :
candidates
.where((c) => c.yearsExperience >= 5)
.forEach((c) => c.interview());
Switch and case
Dart中的switch语句支持整数,字符串或,使用==来比较的编译时常量。比较对象必须都是同一个类的实例(而不是其任何子类型),并且该类不能覆盖==
。 枚举类型switch
语句也支持。
注意: Dart中的Switch语句适用于有限的情况,例如interpreters 或scanners。
每个非空case子句break通常以语句结束。其他有效的方式来结束一个非空的case条目是continue, throw或return。
default当没有case子句匹配时,使用子句执行代码:
var command = 'OPEN';
switch (command) {
case 'CLOSED':
executeClosed();
break;
case 'PENDING':
executePending();
break;
case 'APPROVED':
executeApproved();
break;
case 'DENIED':
executeDenied();
break;
case 'OPEN':
executeOpen();
break;
default:
executeUnknown();
}
以下示例省略break了case子句中的语句,从而产生错误(如果当前case拥有语句,不写break的话,会报错):
var command = 'OPEN';
switch (command) {
case 'OPEN':
executeOpen();
// ERROR: Missing break
case 'CLOSED':
executeClosed();
break;
}
但是,Dart确实支持空case句子,允许一种形式的落空(向下执行):
var command = 'CLOSED';
switch (command) {
case 'CLOSED': // Empty case falls through.
case 'NOW_CLOSED':
// Runs for both CLOSED and NOW_CLOSED.
executeNowClosed();
break;
}
如果你真的想要落空条目,你可以使用一个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;
}
一个case
条目可以有局部变量,作用域是内部的条目的范围。
断言
assert如果布尔条件为false,断言语句中断正常执行,以下为例:
// Make sure the variable has a non-null value.
assert(text != null);
// Make sure the value is less than 100.
assert(number < 100);
// Make sure this is an https URL.
assert(urlString.startsWith('https'));
注意: 断言语句对生产代码没有影响; 他们只是为了发展。Flutter在调试模式下启用断言。 仅限开发的工具(如dartdevc) 通常默认支持断言。一些工具,如dart和dart2js, 通过命令行标志支持断言:
--enable-asserts
。
要将消息附加到断言,请添加一个字符串作为第二个参数。
assert(urlString.startsWith('https'),
'URL ($urlString) should start with "https".');
第一个参数assert
可以是任何解析为布尔值的表达式。如果表达式的值为true,则断言成功并继续执行。如果为false,则断言失败并抛出异常( AssertionError)。
Exceptions
你的Dart代码可以抛出和捕捉异常。异常是错误,表示发生了意外的事情。如果没有捕获到异常,引发异常的隔离会挂起,通常隔离及其程序会终止。
与Java相比,Dart的所有异常都是未经检查的异常。方法不会声明它们可能引发的异常,并且您不需要捕获任何异常。
Dart提供了Exception和Error 类型,以及许多预定义的子类型。当然,您可以定义自己的例外情况。但是,Dart程序可以抛出任何非null对象 - 不仅仅是Exception和Error对象 - 作为异常。
Throw
以下是抛出或引发异常的示例:
throw FormatException('Expected at least 1 section');
你也可以抛出任意对象:
throw 'Out of llamas!';
注意:生产质量代码通常会抛出实现错误或异常的类型 。
因为抛出异常是一个表达式,所以可以在=>语句中以及允许表达式的任何其他地方抛出异常:
void distanceTo(Point other) => throw UnimplementedError();
Catch
要处理可能抛出多种类型异常的代码,可以指定多个catch子句。与抛出对象的类型匹配的第一个catch子句处理异常。如果catch子句未指定类型,则该子句可以处理任何类型的抛出对象:
try {
breedMoreLlamas();
} on OutOfLlamasException {
buyMoreLlamas();
}
正如上面的代码所示,您可以使用on
或catch
或两者兼而有之。使用on
时需要指定异常类型。使用catch
时,你的异常处理程序需要异常对象。
要处理可能抛出多种类型异常的代码,可以指定多个catch子句。与抛出对象的类型匹配的第一个catch子句处理异常。如果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');
}
正如上面的代码所示,您可以使用on
或catch
或两者兼而有之。使用on
时需要指定异常类型。使用catch
时,你的异常处理程序需要异常对象。
您可以指定一个或两个参数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}.');
}
}
Finally
无论是否抛出异常,要确保某些代码运行,请使用finally子句。如果没有catch子句匹配该异常,则在finally子句运行后传播异常:
try {
breedMoreLlamas();
} finally {
// Always clean up, even if an exception is thrown.
cleanLlamaStalls();
}
该finally子句在任何匹配的catch子句之后运行:
try {
breedMoreLlamas();
} catch (e) {
print('Error: $e'); // Handle the exception first.
} finally {
cleanLlamaStalls(); // Then clean up.
}
阅读 dart之旅的Exception部分,了解更多相关信息 。