Dart学习笔记(一)

一个基本的Dart程序

下面的代码中使用了很多Dart最基本的特性:

// 定义一个函数
printInteger(int aNumber) {
  print('The number is $aNumber.'); 
  // 输出到控制台。
}

// 和多数语言一样,main()是程序开始运行的入口。
main() {
  var number = 32; 
  // 声明和初始化一个变量。
  printInteger(number); 
  // 调用上面定义的函数。
}

重要概念

  • 所有你能够赋值给一个变量的都是一个对象,而每个对象都是的一个实例。甚至是数字、函数还有null值都是对象。所有对象都源于一个公共父类Object
  • 虽然Dart是强类型语言,但是类型声明是可选项(即不是必须的),因为Dart可以自己去推断类型。在上面的代码中,变量number就被推断为int类型。当你想明确说没有类型时,可以使用特殊类dynamic。
  • Dart支持泛型,像List<int>,一个整数列表。
  • Dart支持顶层函数,如main(),以及与类绑定的函数(静态方法),还有与对象绑定的函数(实例方法)。甚至可以在函数中创建函数(嵌套函数或局部函数)。
  • 同样,Dart支持顶级变量、静态变量和实例变量。实例变量即对象的属性。
  • 和Java不同的是,Dart没有public、protected和private的关键字。如果一个变量名以下划线“_”开头,说明它是私有变量。
  • 变量名要以字母或者下划线开头。

关键字

待补充。

变量

以下是声明变量并初始化的示例:

var name = 'yzune';

以上的声明没有明确指定变量是什么类型的,由Dart去推断。当然也可以显式地指定变量类型,像下面这样子:

String name = 'yzune';

默认值

未初始化的变量初始值为null,哪怕对于int类型的变量也是如此。这点跟Java不太一样。

final和const

如果你不打算修改一个变量,那么可以在声明的时候使用final或const。一个final变量只能被赋值一次;一个const变量等同于编译时常量(声明为const也意味着是final变量)。

注意:类的属性可以是final,但不可以是const。

声明final变量的示例:

final name = 'yzune';
// 可以不需要指明类型。
// name = 'yzune';
// 但上面这种就不行,因为没有final的关键字。

final String nickname = 'yz';
// 也可以去指明类型。

使用const可以声明一些常量。如果是在定义一个类的成员变量时使用const,应该使用static const进行声明。声明const示例:

const c = 300000000;
// 光速,单位为m/s。

const light_year = c * 365 * 24 * 60 * 60;
// 光年,单位为m。

内置类型

Dart语言对以下类型有着特别的支持(就是内置了的意思):

  • numbers
  • strings
  • booleans
  • lists(arrays
  • maps
  • runes(用于在字符串中表示Unicode字符)
  • symbols

你可以用字面值来初始化任何以上这些特殊类型的对象。比如,'this is a string'就是一个字符串类型字面值,true就是一个布尔类型字面值。

因为Dart中的每个变量都指向一个对象,也就是一个类的实例,所以你通常可以使用类的构造函数来初始化变量。一些内置类型有自己的构造函数。比如,可以使用Map类型的构造函数Map()来创建Map对象:new Map()。

Numbers

Dart中有两种数字类型:int和double。这两种类型都是num类的子类,num类包含了基本的运算,比如四则运算,还有像abs()、ceil()和floor()这些方法。按位运算是在int类中定义的。如果num类和它的子类中没有你想使用的方法,可以引入dart:math库,里面有更多的数学运算方法,比如三角函数。

int

整数值不大于64位,具体取决于平台。在Dart虚拟机上,int的范围为- 2^(63) ~ 2^(63) - 1。如果是将Dart编译为JavaScript,则使用JavaScript对应的类型,范围是- 2^(53) ~ 2^(53) - 1。

double

64位(双精度)浮点数,符合IEEE 754标准。

字符串类型和数字类型相互转换

// 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);
// piAsString的值会是'3.14',在转换时指定了保留两位小数。

Strings

你可以使用单引号或者双引号来创建字符串。

你可以在字符串中通过使用 ${表达式} 来置入一个表达式的值。如果这个表达式本身就是一个变量,那么可以不用添加{}。为了得到表达式的字符串结果,Dart会调用返回对象的toString()方法。

var s = 'string';

assert('Dart的$s还能这样用。' == 'Dart的string还能这样用。');
assert('Dart的${s.toUpperCase()}还能这样用。' == 'Dart的STRING还能这样用。');

注意:==操作符用来测试两个对象是否相等。如果两个字符串包含相同的字符序列,那它们就是相等的。这个跟Java也不一样。



连接字符串的方式有两种,一种是将要连接的字符串相邻并排;另一种是使用+来连接。

var s1 = '字符串' '可以这样'
         '来连接。';

var s2 = '也可以' + '这样子来连接。';



若要表示多行字符串,跟Python相似,可以使用成对的三个单引号或成对的三个双引号。

var s = '''
这是
多行
字符串
''';



让转义字符不发生转义,即显示原始字符串,在引号前加上前缀r即可。

var s = r'这是原始字符串,\n不会被显示为换行符。';

Booleans

为了表示布尔值,Dart有bool类型。bool类型只有两个对象:布尔字面值true和false,它们都是编译时常量。

Dart的类型安全意味着你不能使用类似if (非布尔值)或assert (非布尔值)这样子的代码。规范的代码应该如下:

// 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列表:

var list = [1, 2, 3];

注意:以上代码中,Dart推断list的类型为List<int>。如果尝试将非整数对象添加到这个列表中,会引发错误。

Maps

一般来说,Map是将键和值相关联的对象。键和值都可以是任何类型的对象。每个键都应该是唯一的,只能出现一次。Dart支持使用Map字面值或者map类型来创建Map。

以下是使用Map字面值创建map的例子:

var skills = {
    1: 'Java',
    2: 'Python',
    3: 'Dart'
};

注意:以上代码中,Dart推断skills的类型为Map<int, String>。如果尝试将错误类型的值添加到这个Map中,会引发错误。

还可以使用Map的构造函数来得到相同的效果:

var skills = new Map();
skills[1] = 'Java';
skills[2] = 'Python';
skills[3] = 'Dart';

也可以将新的键值对添加到现有的Map中:

skills[4] = 'C#';

通过skills[键]的方法来访问其中的值,如果其中没有这个键,将会返回null。使用skills.length会得到键值对的个数。

函数

Dart是一种真正的面向对象的语言,所以即使是函数也是对象,并且有一个类型Function。这意味着函数可以分配给变量或作为参数传递给其他函数。你也可以像调用一个函数一样调用一个类的实例。

下面是一个实现函数的实例:

bool haveSkills(String skill){
  return skills.values.contains(skill);
}

对于只包含一个表达式的函数,可以使用简写语法:

bool haveSkills(String skill) => skills.values.contains(skill);

函数可以有两种类型的参数:必需的和可选的。首先列出必需的参数,再列出任何可选参数。

可选参数

可选参数可以是位置型或命名型的,但不能同时是它们。

可选的命名参数

在定义函数的时候,用{参数1, 参数2, …}来指定命名参数。这样在调用函数时,可以使用参数的名称来指定输入的是哪个参数(顺序不同也没关系)。没有输入的参数在函数中被当成null。示例:

void my_print({String name, String age}){
  print("年龄为$age的$name。");
}

main() {
  my_print(age: '21', name: 'yz');
  // 参数的位置可以任意,因为已经由名称去指定了。
}

可选的位置参数

将函数的参数放在[]中,将它们标记为可选的位置参数:

String say(String from, String msg, [String device]) {
  var result = '$from说$msg。';
  if (device != null) {
    result = '$from通过$device说$msg。';
  }
  return result;
}

main() {
  print(say('杨增', '你好'));
  print(say('杨增', '你好', '手机'));
}

// 输出:
// 杨增说你好。
// 杨增通过手机说你好。

默认参数值

可以使用=来为命名参数和位置参数设置默认值。这个默认值必需是编译时常量,如果没有提供默认值,则默认值为null。以下是为命名参数设置默认值的示例:

void my_print({String name = '无名氏', String age = '0'}){
  print("年龄为$age的$name。");
}

main()函数

每个应用程序都必须有一个顶层的main()函数,它可以作为应用程序的入口点。该main()函数返回void并具有List<String>的可选参数。

函数作为第一类对象

你可以将一个函数作为参数传递给另一个函数,例如:

void printElement(int element) {
  print(element);
}

var list = [1, 2, 3];

// 将printElement()作为参数传递过去。
list.forEach(printElement);

匿名函数

大多数函数都有名字,比如main()。你也可以创建匿名函数,可以将一个匿名函数分配给一个变量。例如:

var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});

词法作用域

Dart是基于词法作用域的,词法作用域的函数中遇到既不是形参也不是函数内部定义的局部变量的变量时,去函数定义时的环境中查询。

闭包

这是一个蛮有趣的例子:

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);
}

add2和add4都是闭包,它们共享相同的函数定义,但是保存了不同的词法环境。从本质上讲,makeAdder是一个函数工厂。

闭包很有用,因为它允许将函数与其所操作的某些数据(环境)关联起来。

运算符

Dart定义的运算符如下表,你可以重载这些运算符。在运算符表中,从上往下运算优先级递减。

运算符

当你使用运算符时,你就创建了表达式。下面是一些运算表达式的例子:

a++
a + b
a = b
a == b
c ? a : b
a is T

注意:对于连接两个操作数的运算符,左边的操作数决定了使用哪个版本的运算符。比如有一个Vector对象aVector和一个Point对象aPoint,则aVector + aPoint使用的+为Vector中定义的+。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容