愿你坚持不懈,努力进步,进阶成自己理想的人

—— 2017.09, 写给3年后的自己

Dart学习笔记(一):基础知识与内置类型

一、Dart基础知识

1、程序入口

每个Dart程序都需要有一个main()方法,main()方法是Dart程序执行的入口,因此用Dart写的Hello, world程序如下:

void main() {
    print('Hello, world');
}

2、强制分号

JavaScript不同的是,Dart强制使用分号;

3、自动类型推断

Dart是强类型语言,但是支持自动推断,所以实例中,可以不写void

4、重要概念

  • Dart没有访问控制符(即publicprotectedprivate),但是如果一个标识符以_开头,则表示它在 库内 是私有的
  • 标识符可以以字母或者_开头,后面可以跟其他字符或者数字(注意,Dart不支持$符,也不支持Unicode作为变量名)
  • Dart中所有能够使用变量引用的都是对象,每个对象都是一个的实例,数字、方法、null都是对象,所有对象都继承自Object
  • Dart中函数是可以嵌套声明的


二、Dart变量

1、声明变量

可以使用如下语法[var|<Type>] variableId声明一个变量

2、默认值

声明变量的时候,可以直接进行赋值操作,所赋予的这个值就是默认值。

注意:如果一个变量没有指定初始值,那么 它的初始值是nullint类型的变量初始值也是null,这是因为Dart中数字类型也是对象

3、Final和Const

如果一个变量声明后不打算修改其值,那么可以把这个变量声明为final或者const,他们的区别在于:

  • final只能在初始化时赋值一次,后续值不可以修改
  • const同时也是final,区别在于const更加严格,它是编译时常量,也就是说如果一个变量声明为了const,那么应该是在非运行时就能得到这个值,即:
int getNum() {
    return 1;
}

void main() {
    final a = getNum(); // 没问题
    const b = getNum(); // 报错
}


三、内置类型

Dart内置如下的类型:

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

1、Numbers(数值)

Dart支持两种类型的数值:intdouble,它们都是num类的实例。num类型定义了基本的操作符,如+/-/*//,以及abs()ceil()floor()等函数,而int子类中还定义了位操作符(如>>

注意: 对于int类型,溢出的数值(不在-2^53 ~ 2^53范围内的)的表现和JavaScript中不一样,Dart具有任意精度的证书,但是JavaScript没有

以下是数值类型的一些示例:

int x = 1;
int hex = 0xA;
double y = 1.1;
double z = 1.42e5;

int类型支持位操作:

assert((3 << 1) == 6);
assert((3 >> 1) == 1);
assert((3 | 4) == 7);

字符串可以和数值互转,如:

// String -> init
var one = int.parse('1');
assert(one == 1);

// String -> double
var oneDotOne = double.parse('1.1');
assert(oneDotOne == 1.1);

// int -> String
String strOne = 1.toString();
assert(strOne == '1');

// double -> String
String strOneDotOne = 1.1.toStringAsFixed(1);
assert(strOneDotOne == '1.1');

注意: 数字字面量为编译时常量,只要操作数是常量,那么表达式的结果也是编译时常量

2、Strings(字符串)

Dart字符串是UTF-16编码的字符序列,可以用单引号也可以用双引号:

var s1 = 'single quotes';
var s2 = "double quotes";

Dart同样支持模板字符串,语法为:${expression},如果expression是一个变量,那么可以省略{},即为$varibale。如果表达式的结果是一个对象,那么会调用对象的toString()方法:

var s = 'World';
print('Hello, $s'); // 输出:Hello, World
var s2 = 100;
print('$s2 * 10 = ${s2 * 10}'); // 输出:100 * 10 = 1000

Dart中连接两个字符串,使用的是+符号,即为:

var s1 = 'Hello';
var s2 = 'World';
print(s1 + s2); // 输出:'HelloWorld'

如果要创建多行字符串,可以使用'''或者""",如:

var s = '''
    This is
    a multi-line string
''';

还可以使用r前缀来创建一个原始(raw)字符串,如下:

var s = r'This a raw string, even \n is not special';
// 返回:This a raw string, even \n is not special

字符串字面量是编译时常量。如果一个字符串带有插值,那么如果字符串插值内引用的表达式或者变量计算结果也是编译时常量,那么这个字符串就也是编译时常量,如:

const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';

var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = const [1, 2, 3];

// 以下字符串是合法的,因为它所引用的也都是编译时常量
const validConstString = '$aConstNum $aConstBool $aConstString';
// 但是下面这个就是非法的,因为所引用的不全是编译时常量
const invalidConstString = '$aNum $aBool $aString $aConstList';

3、Booleans(布尔值)

Dart中有名字为bool的类型,它有两个实例对象:truefalse,这两个对象都是编译时常量。当Dart需要一个布尔值的时候,只有true对象才是true,其他的都是false(这是和JavaScript比较不一样的,JavaScripttruthy的值都被认为是true):

var name = 'Ruphi';
if (name) {
    print('Hello, $name');
}

以上代码在生产模式下什么都不会输出,在检查模式下则会抛出一个异常,表示name不是一个布尔值,所以需要特别注意和JavaScript的差异:

if (1) {
    print('JS prints this line');
} else {
    print('Dart in production prints this line.');
    // But in checked mode it throws an exception
}

因此,在Dart中,我们应该 显式地 判断一个变量,确保其计算结果是布尔型的

4、Lists(列表)

Dart中的数组称为List,其字面量语法示例如下:

var list = [1, 2, 3];

List的下标索引范围为0 ~ list.length - 1,所以遍历一个list的方法如下:

int i;
List list = [1, 2, 3, 4, 5];
for (i = 0; i < list.length; ++i) {
    print(list[i]);
}

如果在list字面量前加一个const关键字,那么可以定义为一个不变的list对象(也就是编译时常量),如:

const constantList = const [1, 2, 3];
constantList[1] = 1; // 报错

5、Maps

Dart中用Map来表示键值对相关的对象,键和值可以是任何类型的对象,但是 每个键只能出现一次。Dart中使用map可以通过map字面量Map类型,如:

var fruits = {
    'apple': '苹果',
    'banana': '香蕉'
};
var numMap = {
    1: '壹',
    2: '贰'
};

或者也可以使用构造函数实现一样的功能:

var fruits = new Map();
fruits['apple'] = '苹果';
fruits['banana'] = '香蕉';

const numMap = new Map();
numMap[1] = '壹';
numMap[2] = '贰';

创建完后,是可以继续向map中加入键值对的,也可以访问已加入的键值对,都是采用[]语法:

fruits['pear'] = '梨子';
fruits['apple']; // 返回 '苹果'

可以使用length来访问map中键值对的对数,也可以用const来将map声明为编译时常量

6、Runes

在Dart中,runes代表字符串的UTF-32 UnicodeUnicode为每一个字符、标点符号、表情符号等都定义了一个唯一的数值,但是Dart字符串UTF-16的字符序列,所以如果要表达UTF-32的字符,那么就需要用Runes
通常而言,表示Unicode字符的语法是:\uXXXXXXXX表示的是4个16进制数,而1个16进制数需要用4位来表示,4个XXXX则是16位,即为UTF-16,那么对于UTF-32而言,它需要32位来表示来表示一个Unicode,而对于这些字符,所采用的语法则是:\u{XXXX...},如笑脸的emoji是\u{1f600}
而包含UTF-32字符的字符串,就要采用Runes了,如下:

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

输出如下图所示: