Dart官方文档翻译(一)(Dart之旅)

这篇文章将会向你展示如何使用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 

整数类型。一些其他的内置类型StringListbool

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不具备关键字publicprotectedprivate。如果标识符以下划线(_)开头,则它对其库是私有的。有关详细信息,请参阅 库和可见性
  • 标识符可以以字母或下划线(_)开头,后跟这些字符加数字的任意组合。
  • Dart可以使用_表达式_(具有运行时值)和 语句(不具有)。例如,条件表达式 condition ? expr1 : expr2的值为expr1expr2。将其与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发布后添加的异步支持相关的更新,有限的保留字。不能使用awaityield作为任何函数体中的标识符标记asyncasync*sync*
    表中的所有其他单词都是保留字,不能是标识符。

变量

这是创建变量并初始化它的示例:

var name = 'Bob' ; 

变量存储引用。名为name的变量指向String值为“Bob” 的对象。
name推断变量的类型String,但您可以通过指定它来更改该类型。如果对象不限于单一类型,请按照设计准则指定Objectdynamic类型 。

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的初始化表达式,如上所述。有关详细信息,请参阅不要冗余地使用constconst``baz
您可以更改非final,非const变量的值,即使它曾经有一个const值:

foo = [1, 2, 3]; // Was const [] 

您无法更改const变量的值:

baz = [ 42 ]; //错误:无法为常量变量赋值。 

有关使用const创建常量值的更多信息,请参阅 ListsMapsClasses

内置类型

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标准规定。
这两个intdouble的亚型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.

有关集的更多信息,请参阅 泛型Set

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类型的 Dart Map<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信息。在codeUnitAtcodeUnit属性返回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构造,当必要参数缺少时,编译器会报错。
requaredmate包中定义。可以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是否表示相同的事物,请使用 ==运算符。(在极少数情况下,您需要知道两个对象是否是完全相同的对象,请使用相同的() 函数。)以下是==运算符的工作方式:

  1. 如果_x_或_y_为null,则如果两者都为null则返回true;如果只有一个为null,则返回false。

  2. 返回方法调用的结果 。(这是正确的,运算符,例如在第一个操作数上调用的方法。您甚至可以覆盖许多运算符,包括,正如您在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) 通常默认支持断言。一些工具,如dartdart2js, 通过命令行标志支持断言:--enable-asserts

要将消息附加到断言,请添加一个字符串作为第二个参数。

1assert(urlString.startsWith('https'), 2 'URL ($urlString) should start with "https".');

第一个参数assert可以是任何解析为布尔值的表达式。如果表达式的值为true,则断言成功并继续执行。如果为false,则断言失败并抛出异常( AssertionError)。

Exceptions

你的Dart代码可以抛出和捕捉异常。异常是错误,表示发生了意外的事情。如果没有捕获到异常,引发异常的隔离会挂起,通常隔离及其程序会终止。

与Java相比,Dart的所有异常都是未经检查的异常。方法不会声明它们可能引发的异常,并且您不需要捕获任何异常。

Dart提供了ExceptionError 类型,以及许多预定义的子类型。当然,您可以定义自己的例外情况。但是,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}

正如上面的代码所示,您可以使用oncatch或两者兼而有之。使用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}

正如上面的代码所示,您可以使用oncatch或两者兼而有之。使用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}
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )