这篇文章将会向你展示如何使用Dart的每一个知识点,包括变量,操作符,类,类库,但这是在你已经有一个其他语言的编码经验的前提下。 为了学习Dart更多关于Dart的核心类库,请查看A Tour of the Dart Libraries,当你想知道更多语言特征,挺查阅Dart language specification
你可以通过DartPad 来尝试运行Dart代码:Open DartPad
一个基本的Dart程序
1// Define a function. 2printInteger(int aNumber) { 3 print('The number is $aNumber.'); // Print to console. 4} 5 6// This is where the app starts executing. 7main() { 8 var number = 42; // Declare and initialize a variable. 9 printInteger(number); // Call a function. 10}
以下是此程序使用的适用于所有(或几乎所有)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中的其他所有都是对象。
1int lineCount ; 2assert (lineCount == null );
注意:生产环境代码中将忽略assert() 调用。在开发期间, 除非_条件_为真,否则抛出异常。有关详细信息,请参阅断言。
Final and const
如果您从未打算更改变量,请使用final或const代替var或替代类型。最终变量只能设置一次; const变量是编译时常量。(Const变量是隐式final的。)final的顶级或类变量在第一次使用时被初始化。
以下是创建和设置最终变量的示例:
1final name = 'Bob'; // Without a type annotation 2final String nickname = 'Bobby';
您无法更改最终变量的值:
name = 'Alice' ; //错误:最终变量只能设置一次。
使用const为您要为变量的编译时间常数。如果const变量在类级别,请声明为static const。在声明变量的地方,将值设置为编译时常量,例如数字或字符串字面值,const变量或对常数进行算术运算的结果:
1const bar = 1000000; // 压力单位 (dynes/cm2) 2const double atm = 1.01325 * bar; // 标准大气压
该const关键字不只是声明常数变量。您还可以使用它来创建常量值,以及声明创建常量值的构造函数。任何变量都可以具有常量值。
1var foo = const []; 2final bar = const []; 3const 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库可能会定义。
整数是没有小数点的数字。以下是定义整型字面值的一些示例:
1var x = 1; 2var hex = 0xDEADBEEF;
如果数字包含小数,则为双精度数。以下是定义双精度字面值的一些示例:
1var y = 1.1; 2var exponents = 1.42e5;
从Dart 2.1开始,必要时整型字面值会自动转换为双精度数:
double z = 1; // Equivalent to double z = 1.0.
版本说明: 在Dart 2.1之前,在双精度数变量赋值为整型字面值是错误的。
以下是将字符串转换为数字的方法,反之亦然:
1// String -> int 2var one = int.parse('1'); 3assert(one == 1); 4 5// String -> double 6var onePointOne = double.parse('1.1'); 7assert(onePointOne == 1.1); 8 9// int -> String 10String oneAsString = 1.toString(); 11assert(oneAsString == '1'); 12 13// double -> String 14String piAsString = 3.14159.toStringAsFixed(2); 15assert(piAsString == '3.14');
int类型具有传统的按位移位(<<,>>),AND(&)和OR(|)运算符。例如:
1assert((3 << 1) == 6); // 0011 << 1 == 0110 2assert((3 >> 1) == 1); // 0011 >> 1 == 0001 3assert((3 | 4) == 7); // 0011 | 0100 == 0111
字面值数字是编译时常量。许多算术表达式也是编译时常量,因为它们的操作数是编译为数字的编译时常量。
1const msPerSecond = 1000; 2const secondsUntilRetry = 5; 3const msUntilRetry = secondsUntilRetry * msPerSecond;
Strings
Dart字符串是一系列UTF-16代码单元。您可以使用单引号或双引号来创建字符串:
1var s1 = 'Single quotes work well for string literals.'; 2var s2 = "Double quotes work just as well."; 3var s3 = 'It\'s easy to escape the string delimiter.'; 4var s4 = "It's even easier to use the other delimiter.";
您可以使用表达式将表达式的值放在字符串中 。如果表达式是标识符,则可以跳过{}。要获取与对象相对应的字符串,Dart会调用该对象的方法。${expression}toString()
1var s = 'string interpolation'; 2 3assert('Dart has $s, which is very handy.' == 4 'Dart has string interpolation, ' + 5 'which is very handy.'); 6assert('That deserves all caps. ' + 7 '${s.toUpperCase()} is very handy!' == 8 'That deserves all caps. ' + 9 'STRING INTERPOLATION is very handy!');
注: 在==两个物体操作测试是否是等价的。如果两个字符串包含相同的代码单元序列,则它们是等效的。(这一点和java是不同的,java,String的==运算符,只有当地址一样,字面值一样,才能使相等的,dart String 的== 相当于java String的equal)
您可以使用相邻的字符串字面值或+ 运算符来连接字符串:
1var s1 = 'String ' 2 'concatenation' 3 " works even over line breaks."; 4assert(s1 == 5 'String concatenation works even over ' 6 'line breaks.'); 7 8var s2 = 'The + operator ' + 'works, as well.'; 9assert(s2 == 'The + operator works, as well.');
创建多行字符串的另一种方法:使用带有单引号或双引号的三引号:
1var s1 = ''' 2You can create 3multi-line strings like this one. 4'''; 5 6var s2 = """This is also a 7multi-line string.""";
您可以通过为其添加前缀来创建“raw”字符串r:
var s = r'In a raw string, not even \n gets special treatment.';
有关如何在字符串中表示Unicode字符的详细信息,请参阅Runes。
字符串字面值是编译时常量,因为任何插值表达式是一个编译时常量,其值为null或数值,字符串或布尔值。
1// These work in a const string. 2const aConstNum = 0; 3const aConstBool = true; 4const aConstString = 'a constant string'; 5 6// These do NOT work in a const string. 7var aNum = 0; 8var aBool = true; 9var aString = 'a string'; 10const aConstList = [1, 2, 3]; 11 12const validConstString = '$aConstNum $aConstBool $aConstString'; 13// const invalidConstString = '$aNum $aBool $aString $aConstList';
有关使用字符串的更多信息,请参阅 字符串和正则表达式。
Booleans
为了表示布尔值,Dart具有一个名为的类型bool。只有两个对象具有bool类型:boolean 字面值 true和false,它们都是编译时常量。
Dart的类型安全意味着你不能使用像或 那样的代码,如下:
1if (nonbooleanValue) 2 assert (nonbooleanValue)
应该明确能计算出值:
1// Check for an empty string. 2var fullName = ''; 3assert(fullName.isEmpty); 4 5// Check for zero. 6var hitPoints = 0; 7assert(hitPoints <= 0); 8 9// Check for null. 10var unicorn; 11assert(unicorn == null); 12 13// Check for NaN. 14var iMeantToDoThis = 0 / 0; 15assert(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元素:
1var list = [1, 2, 3]; 2assert(list.length == 3); 3assert(list[1] == 2); 4 5list[1] = 1; 6assert(list[1] == 1);
要创建一个编译时常量const的List,请在List文字前添加_const_:
1var constantList = const [1, 2, 3]; 2// 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:
1var names = <String>{}; 2// Set<String> names = {}; // This works, too. 3// var names = {}; // Creates a map, not a set.
Set还是Map? Map字面值的语法类似于Set字面值的语法。由于Map的优先级靠前,因此{}默认为Map类型。如果您忘记了类型注释{}或它所分配的变量,则Dart会创建一个类型的对象Map<dynamic, dynamic>。
使用add()或addAll()方法将项添加到现有集:
1var elements = <String>{}; 2elements.add('fluorine'); 3elements.addAll(halogens);
使用.length得到的一组Set的数量:
1var elements = <String>{}; 2elements.add('fluorine'); 3elements.addAll(halogens); 4assert(elements.length == 5);
要创建一个编译时常量const的集合,请在set literal之前添加const:
1final constantSet = const { 2 'fluorine', 3 'chlorine', 4 'bromine', 5 'iodine', 6 'astatine', 7}; 8// constantSet.add('helium'); // Uncommenting this causes an error.
Maps
通常,映射是关联键和值的对象。键和值都可以是任何类型的对象。每个_键_只出现一次,但您可以多次使用相同的_值_。Map的Dart支持由Map字面值和Map类型提供。
这里有几个简单的Dart Map例子,使用Map字面值创建:
1var gifts = { 2 // Key: Value 3 'first': 'partridge', 4 'second': 'turtledoves', 5 'fifth': 'golden rings' 6}; 7 8var nobleGases = { 9 2: 'helium', 10 10: 'neon', 11 18: 'argon', 12};
Dart推断出
gifts的类型Map<String, String>以及nobleGases类型的 DartMap<int, String>。如果您尝试将错误类型的值添加到任一映射,则分析器或运行时会引发错误。有关更多信息,请阅读 类型推断。
您可以使用Map构造函数创建相同的对象:
1var gifts = Map(); 2gifts['first'] = 'partridge'; 3gifts['second'] = 'turtledoves'; 4gifts['fifth'] = 'golden rings'; 5 6var nobleGases = Map(); 7nobleGases[2] = 'helium'; 8nobleGases[10] = 'neon'; 9nobleGases[18] = 'argon';
注意: 您可能想添加
new而不仅仅是Map()。从Dart 2开始,new关键字是可选的。有关详细信息,请参阅使用构造函数。
像在JavaScript中一样,将新的键值对添加到现有Map中:
1var gifts = {'first': 'partridge'}; 2gifts['fourth'] = 'calling birds'; // Add a key-value pair
以与在JavaScript中相同的方式从Map中检索值:
1var gifts = {'first': 'partridge'}; 2assert(gifts['first'] == 'partridge');
如果您查找不在Map中的键,则会得到null作为回报:
1var gifts = {'first': 'partridge'}; 2assert(gifts['fifth'] == null);
使用.length得到的映射中的键值对的数量:
1var gifts = {'first': 'partridge'}; 2gifts['fourth'] = 'calling birds'; 3assert(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位代码点之间的关系。
1main() { 2 var clapping = '\u{1f44f}'; 3 print(clapping); 4 print(clapping.codeUnits); 5 print(clapping.runes.toList()); 6 7 Runes input = new Runes( 8 '\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}'); 9 print(new String.fromCharCodes(input)); 10}
注意: 使用列表操作操作符文时要小心。这种方法很容易使字符串解体,具体取决于特定的语言,字符集和操作。有关更多信息,请参阅 如何在Dart中反转字符串?在Stack Overflow上。
Symbols
Symbol对象表示在Dart程序中声明的运算符或标识符。您可能永远不需要使用符号,但它们对于按名称引用标识符的API非常有用,因为缩小会更改标识符名称而不会更改标识符符号。要获取标识符的符号,请使用符号文字,它只是#后跟标识符:(这一段实在不会翻译了- -)
要获取标识符的符号,请使用符号文字, #后面跟着标识符:
1 #radix 2 #bar
Symbols文字是编译时常量。
Functions
Dart是一种真正的面向对象语言,因此即使是函数也是对象并且具有类型Function。 这意味着函数可以分配给变量或作为参数传递给其他函数。您也可以调用Dart类的实例,就好像它是一个函数一样。有关详细信息,请参阅Callable classes
以下是实现函数的示例:
1bool isNoble(int atomicNumber) { 2 return _nobleGases[atomicNumber] != null; 3}
虽然Effective Dart建议 为公共API声明返回值类型,但如果省略类型,该函数仍然有效:
1isNoble(atomicNumber) { 2 return _nobleGases[atomicNumber] != null; 3}
对于只包含一个表达式的函数,可以使用简写语法:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
这个 => 是{ return expr; }的速记 . 。有时将=> 表示法称为箭头语法。
只有表达式(而不是语句)能够出现在箭头(=>)和分号(;)之间。例如,您不能在其中放置if语句,但可以使用条件表达式。
函数可以有两种类型的参数:必需和可选。首先列出所需参数,然后列出任何可选参数。命名的可选参数也可以标记为@required。有关详细信息,请参阅下一节。
可选参数
可选参数可以是位置参数,也可以是名称参数,但不能同时包含
可选的名称参数
调用函数时,可以使用指定名称参数 。例如:paramName: value
enableFlags (bold :true ,hidden :false );
定义函数时,用于 指定名称参数:{param1, param2, …}
1/// Sets the [bold] and [hidden] flags ... 2void 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。
可选的位置参数
包装一组函数参数将[]它们标记为可选的位置参数:
1String say(String from, String msg, [String device]) { 2 var result = '$from says $msg'; 3 if (device != null) { 4 result = '$result with a $device'; 5 } 6 return result; 7}
这是一个在没有可选参数的情况下调用此函数的示例:
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
以下是使用第三个参数调用此函数的示例:
1assert(say('Bob', 'Howdy', 'smoke signal') == 2 'Bob says Howdy with a smoke signal');
默认参数值
您的函数可用于=定义命名和位置参数的默认值。默认值必须是编译时常量。如果未提供默认值,则默认值为null。
以下是为命名参数设置默认值的示例:
1/// Sets the [bold] and [hidden] flags ... 2void enableFlags({bool bold = false, bool hidden = false}) {...} 3 4// bold will be true; hidden will be false. 5enableFlags(bold: true);
弃用注释: 旧代码可能使用冒号(
:)而不是=设置命名参数的默认值。原因是最初只:支持命名参数。该支持可能已被弃用,因此我们建议您 使用=指定默认值。
下一个示例显示如何设置位置参数的默认值:
1String say(String from, String msg, 2 [String device = 'carrier pigeon', String mood]) { 3 var result = '$from says $msg'; 4 if (device != null) { 5 result = '$result with a $device'; 6 } 7 if (mood != null) { 8 result = '$result (in a $mood mood)'; 9 } 10 return result; 11} 12 13assert(say('Bob', 'Howdy') == 14 'Bob says Howdy with a carrier pigeon');
您还可以将List或Map作为默认值传递。以下示例定义了一个函数,该函数doStuff()指定参数的默认List和list 参数的默认Map gifts。
1void doStuff( 2 {List<int> list = const [1, 2, 3], 3 Map<String, String> gifts = const { 4 'first': 'paper', 5 'second': 'cotton', 6 'third': 'leather' 7 }}) { 8 print('list: $list'); 9 print('gifts: $gifts'); 10}
main()函数
每个应用程序都必须具有顶级main()功能,该功能用作应用程序的入口点。该main()函数返回void并具有List<String>参数的可选参数。
以下main()是Web应用程序功能的示例:
1void main() { 2 querySelector('#sample_text_id') 3 ..text = 'Click me!' 4 ..onClick.listen(reverseText); 5}
注意:
..前面代码中 的语法称为级联。使用级联,您可以对单个对象的成员执行多个操作。
以下main()是带参数的命令行应用程序的函数示例:
1// Run the app like this: dart args.dart 1 test 2void main(List<String> arguments) { 3 print(arguments); 4 5 assert(arguments.length == 2); 6 assert(int.parse(arguments[0]) == 1); 7 assert(arguments[1] == 'test'); 8}
您可以使用args库来定义和解析命令行参数。
Function 作为第一类对象的功能
您可以将函数作为参数传递给另一个函数。例如:
1void printElement(int element) { 2 print(element); 3} 4 5var list = [1, 2, 3]; 6 7// Pass printElement as a parameter. 8list.forEach(printElement);
您还可以为变量分配函数,例如:
1var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!'; 2assert(loudify('hello') == '!!! HELLO !!!');
此示例使用了匿名函数。更多关于下一节的内容。
匿名函数
大多数函数都被命名,例如main()或printElement()。您还可以创建一个名为匿名函数的无名函数,有时也可以创建一个lambda或闭包。您可以为变量分配匿名函数,以便例如可以在集合中添加或删除它。
匿名函数看起来类似于命名函数 - 零个或多个参数,在逗号和括号之间用逗号和可选类型注释分隔。
后面的代码块包含函数的主体:
([[Type] param1[, …]]) {
codeBlock;
};
以下示例使用无类型参数item定义匿名函数,为List中的每个项调用的函数将打印一个包含指定索引处的值的字符串。
1var list = ['apples', 'bananas', 'oranges']; 2list.forEach((item) { 3 print('${list.indexOf(item)}: $item'); 4});
如果函数只包含一个语句,则可以使用箭头表示法缩短它。
1list.forEach( 2 (item) => print('${list.indexOf(item)}: $item'));
作用域
Dart是有作用域的语言,这意味着变量的范围是静态确定的,只需通过代码的布局。您可以“向外跟随花括号”以查看变量是否在范围内。
以下是每个范围级别包含变量的嵌套函数示例:
1bool topLevel = true; 2 3void main() { 4 var insideMain = true; 5 6 void myFunction() { 7 var insideFunction = true; 8 9 void nestedFunction() { 10 var insideNestedFunction = true; 11 12 assert(topLevel); 13 assert(insideMain); 14 assert(insideFunction); 15 assert(insideNestedFunction); 16 } 17 } 18}
请注意如何使用nestedFunction()每个级别的变量,一直到顶级。
闭包
一个闭包是能够访问在其作用域的变量的函数的对象,即使当函数用于其原来的范围之外。
函数可以关闭周围范围中定义的变量。在以下示例中,makeAdder()捕获变量addBy。无论返回的函数在哪里,它都记录addBy。
1/// Returns a function that adds [addBy] to the 2/// function's argument. 3Function makeAdder(num addBy) { 4 return (num i) => addBy + i; 5} 6 7void main() { 8 // Create a function that adds 2. 9 var add2 = makeAdder(2); 10 11 // Create a function that adds 4. 12 var add4 = makeAdder(4); 13 14 assert(add2(3) == 5); 15 assert(add4(3) == 7); 16}
测试函数是否相等
以下是测试顶级函数,静态方法和实例方法的相等性的示例:
1void foo() {} // A top-level function 2 3class A { 4 static void bar() {} // A static method 5 void baz() {} // An instance method 6} 7 8void main() { 9 var x; 10 11 // Comparing top-level functions. 12 x = foo; 13 assert(foo == x); 14 15 // Comparing static methods. 16 x = A.bar; 17 assert(A.bar == x); 18 19 // Comparing instance methods. 20 var v = A(); // Instance #1 of A 21 var w = A(); // Instance #2 of A 22 var y = w; 23 x = w.baz; 24 25 // These closures refer to the same instance (#2), 26 // so they're equal. 27 assert(y.baz == x); 28 29 // These closures refer to different instances, 30 // so they're unequal. 31 assert(v.baz != w.baz); 32}
返回值
1foo() {} 2assert(foo() == null);
运算符
Dart定义下表中显示的运算符。您可以覆盖许多运算符,如在 可重写运算符。
描述
运算符
一元后缀
expr++ expr-- () [] . ?.
一元前缀
-expr !expr ~expr ++expr --expr
乘
* / % ~/
添加
+ -
位移
<< >> >>>
按位与
&
按位异或
^
按位或
|
大小和类型测试
>= > <= < as is is!
相等性
== !=
逻辑与
&&
逻辑或
||
非空
??
?表达式
expr1 ? expr2 : expr3
级联
..
再运算
= *= /= += -= &= ^= etc.
警告: 运算符优先级是Dart解析器行为。有关明确的规则,请参阅Dart语言规范中的语法
使用运算符时,可以创建表达式。以下是运算符表达式的一些示例:
1a++ 2a + b 3a = b 4a == b 5c ? a : b 6a is T
在运算符表中,每个运算符的优先级高于其后的行中的运算符。例如,乘法运算符的%优先级高于(因此之前执行)等于运算符==,它的优先级高于逻辑AND运算符&&。该优先级意味着以下两行代码执行相同的方式:
1// Parentheses improve readability. 2if ((n % i == 0) && (d % i == 0)) ... 3 4// Harder to read, but equivalent. 5if (n % i == 0 && d % i == 0) ...
警告: 对于处理两个操作数的运算符,最左边的操作数确定使用哪个对象的运算符。例如,如果您有Vector对象和Point对象,则aVector + aPoint使用Vector对象的+。
算术运算符
Dart支持通常的算术运算符,如下表所示。
Operator
Meaning
加
–
减
-expr
负号
*
乘法
/
除法
~/
整除
%
获取整数除法的余数(模数)
例:
1assert(2 + 3 == 5); 2assert(2 - 3 == -1); 3assert(2 * 3 == 6); 4assert(5 / 2 == 2.5); // Result is a double 5assert(5 ~/ 2 == 2); // Result is an int 6assert(5 % 2 == 1); // Remainder 7 8assert('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)
1var a, b; 2 3a = 0; 4b = ++a; // Increment a before b gets its value. 5assert(a == b); // 1 == 1 6 7a = 0; 8b = a++; // Increment a AFTER b gets its value. 9assert(a != b); // 1 != 0 10 11a = 0; 12b = --a; // Decrement a before b gets its value. 13assert(a == b); // -1 == -1 14 15a = 0; 16b = a--; // Decrement a AFTER b gets its value. 17assert(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对使用该对象的表达式后跟对象的测试的简写。例如,请考虑以下代码:
1if (emp is Person) { 2 // Type check 3 emp.firstName = 'Bob'; 4}
您可以使用as运算符缩短代码:
(emp as Person).firstName = 'Bob';
注意: 代码不相同。如果emp为null或不是Person,则第一个示例(with is)不执行任何操作; 第二个(带as)抛出一个异常。
赋值操作符
如您所见,您可以使用=运算符分配值。要仅在变量为null时分配,请使用??=运算符,如下,如果为null就执行f
1// Assign value to a 2a = value; 3// Assign value to b if b is null; otherwise, b stays the same 4b ??= value;
复合赋值运算符,例如+=将操作与赋值组合在一起。
=
–=
/=
%=
>>=
^=
+=
*=
~/=
<<=
&=
|=
以下是复合赋值运算符的工作原理:
复合赋值
等价表达
对于运营商op:
a op= b
a = a op b
例:
a += b
a = a + b
以下示例使用赋值和复合赋值运算符:
1var a = 2; // Assign using = 2a *= 3; // Assign and multiply: a = a * 3 3assert(a == 6);
逻辑运算符
您可以使用逻辑运算符反转或组合布尔表达式
操作者
含义
!expr
反转以下表达式(将false更改为true,反之亦然)
||
逻辑或
&&
逻辑AND
以下是使用逻辑运算符的示例:
1if (!done && (col == 0 || col == 3)) { 2 // ...Do something... 3}
按位和移位运算符
您可以在Dart中操纵数字的各个位。通常,您将使用这些按位和移位运算符和整数。
操作者
含义
&
和
|
或
^
异或
~expr
一元逐位补码(0s变为1s; 1s变为0s)
<<
向左位移
>>
向右位移
这是使用按位和移位运算符的示例:
1final value = 0x22; 2final bitmask = 0x0f; 3 4assert((value & bitmask) == 0x02); // AND 5assert((value & ~bitmask) == 0x20); // AND NOT 6assert((value | bitmask) == 0x2f); // OR 7assert((value ^ bitmask) == 0x2d); // XOR 8assert((value << 4) == 0x220); // Shift left 9assert((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';
前面的例子至少可以用其他两种方式编写,但不够简洁:
1// Slightly longer version uses ?: operator. 2String playerName(String name) => name != null ? name : 'Guest'; 3 4// Very long version uses if-else statement. 5String playerName(String name) { 6 if (name != null) { 7 return name; 8 } else { 9 return 'Guest'; 10 } 11}
级联符号..
(Cascades)级联符号 .. 允许您对同一对象进行一系列操作。除了函数调用,您还可以访问同一对象上的字段。这通常可以为您节省创建临时变量的步骤,并允许您编写更多流畅的代码。
请考虑以下代码:
1querySelector('#confirm') // Get an object. 2 ..text = 'Confirm' // Use its members. 3 ..classes.add('important') 4 ..onClick.listen((e) => window.alert('Confirmed!'));
第一个方法调用,querySelector()返回一个选择器对象。级联表示法后面的代码对此选择器对象进行操作,忽略可能返回的任何后续值。
前面的例子相当于:
1var button = querySelector('#confirm'); 2button.text = 'Confirm'; 3button.classes.add('important'); 4button.onClick.listen((e) => window.alert('Confirmed!'));
您也可以嵌套您的级联。例如:
1final addressBook = (AddressBookBuilder() 2 ..name = 'jenny' 3 ..email = 'jenny@example.com' 4 ..phone = (PhoneNumberBuilder() 5 ..number = '415-555-0100' 6 ..label = 'home') 7 .build()) 8 .build();
小心在返回实际对象的函数上构造级联。例如,以下代码失败:
1var sb = StringBuffer(); 2sb.write('foo') 3 ..write('bar'); // Error: method 'write' isn't defined for 'void'. 4//返回为void的不能再构造级联 5.setTitle 6.setView 7.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语句的语句,如下一个示例所示。另见条件表达式。
1if (isRaining()) { 2 you.bringRainCoat(); 3} else if (isSnowing()) { 4 you.wearJacket(); 5} else { 6 car.putTopDown(); 7}
for循环
您可以使用标准for循环进行迭代。例如:
1var message = StringBuffer('Dart is fun'); 2for (var i = 0; i < 5; i++) { 3 message.write('!'); 4}
Dart for循环内部的闭包捕获了索引的值,避免了JavaScript中常见的陷阱。例如,考虑:
1var callbacks = []; 2for (var i = 0; i < 2; i++) { 3 callbacks.add(() => print(i)); 4} 5callbacks.forEach((c) => c());
如预期一样,会输出0然后1。相反,在js里会打印出2个2。
如果要迭代的对象是Iterable,则可以使用 forEach()方法。forEach()如果您不需要知道当前的迭代计数器,则使用是一个不错的选择:
candidates.forEach((candidate) => candidate.interview());
像List和Set这样for-in的可迭代类也支持迭代的形式 :
1var collection = [0, 1, 2]; 2for (var x in collection) { 3 print(x); // 0 1 2 4}
While and do-while
一个while循环在执行循环内的逻辑之前,先计算表达式是否符合条件:
1while (!isDone()) { 2 doSomething(); 3}
一个while循环先计算表达式是否符合条件,然后执行循环内的逻辑:
1do { 2 printLine(); 3} while (!atEndOfPage());
Break and continue
使用break停止循环:
1while (true) { 2 if (shutDownRequested()) break; 3 processIncomingRequests(); 4}
使用continue跳到下一个循环迭代:
1for (int i = 0; i < candidates.length; i++) { 2 var candidate = candidates[i]; 3 if (candidate.yearsExperience < 5) { 4 continue; 5 } 6 candidate.interview(); 7}
如果您使用Iterable(如列表或集合),则可能会以不同的方式编写该示例 :
1candidates 2 .where((c) => c.yearsExperience >= 5) 3 .forEach((c) => c.interview());
Switch and case
Dart中的switch语句支持整数,字符串或,使用==来比较的编译时常量。比较对象必须都是同一个类的实例(而不是其任何子类型),并且该类不能覆盖==。 枚举类型switch语句也支持。
注意: Dart中的Switch语句适用于有限的情况,例如interpreters 或scanners。
每个非空case子句break通常以语句结束。其他有效的方式来结束一个非空的case条目是continue, throw或return。
default当没有case子句匹配时,使用子句执行代码:
1var command = 'OPEN'; 2switch (command) { 3 case 'CLOSED': 4 executeClosed(); 5 break; 6 case 'PENDING': 7 executePending(); 8 break; 9 case 'APPROVED': 10 executeApproved(); 11 break; 12 case 'DENIED': 13 executeDenied(); 14 break; 15 case 'OPEN': 16 executeOpen(); 17 break; 18 default: 19 executeUnknown(); 20}
以下示例省略break了case子句中的语句,从而产生错误(如果当前case拥有语句,不写break的话,会报错):
1var command = 'OPEN'; 2switch (command) { 3 case 'OPEN': 4 executeOpen(); 5 // ERROR: Missing break 6 7 case 'CLOSED': 8 executeClosed(); 9 break; 10}
但是,Dart确实支持空case句子,允许一种形式的落空(向下执行):
1var command = 'CLOSED'; 2switch (command) { 3 case 'CLOSED': // Empty case falls through. 4 case 'NOW_CLOSED': 5 // Runs for both CLOSED and NOW_CLOSED. 6 executeNowClosed(); 7 break; 8}
如果你真的想要落空条目,你可以使用一个continue声明和一个标签:
1var command = 'CLOSED'; 2switch (command) { 3 case 'CLOSED': 4 executeClosed(); 5 continue nowClosed; 6 // Continues executing at the nowClosed label. 7 8 nowClosed://这里是一个标签,可以写任意标识符 9 case 'NOW_CLOSED': 10 // Runs for both CLOSED and NOW_CLOSED. 11 executeNowClosed(); 12 break; 13}
一个case条目可以有局部变量,作用域是内部的条目的范围。
断言
assert如果布尔条件为false,断言语句中断正常执行,以下为例:
1// Make sure the variable has a non-null value. 2assert(text != null); 3 4// Make sure the value is less than 100. 5assert(number < 100); 6 7// Make sure this is an https URL. 8assert(urlString.startsWith('https'));
注意: 断言语句对生产代码没有影响; 他们只是为了发展。Flutter在调试模式下启用断言。 仅限开发的工具(如dartdevc) 通常默认支持断言。一些工具,如dart和dart2js, 通过命令行标志支持断言:
--enable-asserts。
要将消息附加到断言,请添加一个字符串作为第二个参数。
1assert(urlString.startsWith('https'), 2 '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子句未指定类型,则该子句可以处理任何类型的抛出对象:
1try { 2 breedMoreLlamas(); 3} on OutOfLlamasException { 4 buyMoreLlamas(); 5}
正如上面的代码所示,您可以使用on或catch或两者兼而有之。使用on时需要指定异常类型。使用catch时,你的异常处理程序需要异常对象。
要处理可能抛出多种类型异常的代码,可以指定多个catch子句。与抛出对象的类型匹配的第一个catch子句处理异常。如果catch子句未指定类型,则该子句可以处理任何类型的抛出对象:
1try { 2 breedMoreLlamas(); 3} on OutOfLlamasException { 4 // A specific exception 5 buyMoreLlamas(); 6} on Exception catch (e) { 7 // Anything else that is an exception 8 print('Unknown exception: $e'); 9} catch (e) { 10 // No specified type, handles all 11 print('Something really unknown: $e'); 12}
正如上面的代码所示,您可以使用on或catch或两者兼而有之。使用on时需要指定异常类型。使用catch时,你的异常处理程序需要异常对象。
您可以指定一个或两个参数catch()。第一个是抛出的异常,第二个是堆栈跟踪(StackTrace对象)。
1try { 2 // ··· 3} on Exception catch (e) { 4 print('Exception details:\n $e'); 5} catch (e, s) { 6 print('Exception details:\n $e'); 7 print('Stack trace:\n $s'); 8}
要部分处理异常,同时允许它传播,请使用rethrow关键字。
1void misbehave() { 2 try { 3 dynamic foo = true; 4 print(foo++); // Runtime error 5 } catch (e) { 6 print('misbehave() partially handled ${e.runtimeType}.'); 7 rethrow; // Allow callers to see the exception. 8 } 9} 10 11void main() { 12 try { 13 misbehave(); 14 } catch (e) { 15 print('main() finished handling ${e.runtimeType}.'); 16 } 17}
Finally
无论是否抛出异常,要确保某些代码运行,请使用finally子句。如果没有catch子句匹配该异常,则在finally子句运行后传播异常:
1try { 2 breedMoreLlamas(); 3} finally { 4 // Always clean up, even if an exception is thrown. 5 cleanLlamaStalls(); 6}
该finally子句在任何匹配的catch子句之后运行:
1try { 2 breedMoreLlamas(); 3} catch (e) { 4 print('Error: $e'); // Handle the exception first. 5} finally { 6 cleanLlamaStalls(); // Then clean up. 7}
