Flutter Lesson 2: Dart 语法简单介绍

Dart语法Dart中文】可以说是Google因为为Flutter量身定做的一门语言,所以,我们之前基本上都没有接触过这门语言,在入手Flutter坑的时候,还必须了解一下Dart的语言特性。

Dart官网Dart中文】上面已经有了很详细的说明,我这里只是见多的说说,提取一些重点,以便自己复习以及你们学习。
v

main

每一个.Dart文件都会有一个mian()函数,这个称之为程序的入口文件,这就像是前端ES6语法中的export一样,都要有一个输出。其实,Dart的设计就有一点遵循前端的ES6语法,不过他还结合了一些c语言

1
2
3
4
5
6
// 程序的入口文件
main() {
/* 或者用这种注释 */
// main函数输出了一点东西,print 控制台输出
print('我是main函数,我是程序执行的入口');
}

代码都可以在DartPad上面运行,你也可以自行修改代码,不过不可能需要开着VPN才可以访问这个网站。

Dart关键字

关键字

关键字的意思就不用多说了,注意上图中的关键字,在变量命名的时候不要使用就行了。

重要的概念(重要)

  • 在Dart中,所有的能够使用变量引用的都是对象,每一个对象都有一个实例,包括但不限于数字,方法,字符串,null。所有的对象都集成于Object类。这个需要注意,这个和JavaScript中的变量还是有很大的差别的
  • 虽然Dart是强类型语言,但变量类型是可选的因为Dart可以自动推断变量类型
  • Dart支持顶层方法(如main方法),也支持类方法或对象方法,同时你也可以在方法内部创建方法
  • Dart支持顶层变量,也支持类变量或对象变量
  • Dart中的私有变量用_开头,有点像JS中我们自定义函数的时候默认_开头的为私有变量
  • Dart中变量可以以字母或下划线开头,后面跟着任意组合的字符或数字,不能使用$,这里面的$一般用于字符串拼接

变量

每一种编程语言都会有变量,Dart也不例外,不过Dart的区别就是其声明变量的方式有多种。

var

最简单的就是使用var关键字,你也可以使用dynamic,表示没有指定变量类型

1
2
var name = 'tal';
dynamic number = 0.5;

使用var定义变量,最好是定义局部变量

默认值

如果你在定义了一个变量以后,同时并没有对其进行赋值,那么,这个变量最终的类型会是null(注意不是undefined)。

因为前面我已以及说过了,Dart中所有的变量引用都是对象。

可选类型

在声明变量的时候我们可以加上他的类型,这个是google从TS中吸取到的经验

1
2
3
int number = 1;

String name = 'tal';

使用这种方式定义变量可以是全局变量和局部变量,但是如果是使用var定义变量,最好是用于定义局部变量。

Dart中内置了以下几种类型

  • Number
  • String
  • Boolean
  • List ( means array )
  • Map
  • Rune ( 用于在字符串中表示 Unicode 字符 )
  • Symbol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
void main() {
dynamic a = '没有指定变量类型';
print(a); // 没有指定变量类型

// number
var na = 1;
int nb = 2;
double nc = 1.2;
print(na); // 1
print(nb); // 2
print(nc); // 1.2


// String
var sa = 'hello';
String sb = 'world';
print(sa); // hello
print(sb); // world
// 字符串插值,当然,你也可以使用 + 进行字符串拼接
print('$sa ${sb}'); // hello world

// bool
var bt = true;
bool bf = false;

// list
var arr = [1, 'a', false];
List<String> arr1 = ['a', 'b', 'c'];
List<dynamic> arr2 = [1, 'a', true];
print(arr); // [1, a, false]
print(arr1); // [a, b, c]
print(arr2); // [1, a, true]

// map Map 是一个键值对相关的对象,有点像JS中的Object
var person = {
"name": '踏浪',
"sex": '男'
};
print(person); // {name: 踏浪, sex: 男}

var company = new Map();
company['leader'] = '踏浪';
company['money'] = 10000;
print(company); // {leader: 踏浪, money: 10000}

// runes,Dart 中 使用runes 来获取UTF-32字符集的字符。String的 codeUnitAt and codeUnit属性可以获取UTF-16字符集的字符
var clapping = '\u{1f44f}';
print(clapping); // 打印的是拍手emoji的表情

// symbols 使用 # 符号获取你 new 的这一个 symbol
// 类似于JS中的Symbol,获取方式有点不同。在Dart项目中,获取你从来用不到Symbol
print(#s == new Symbol("s")); // true
}

需要注意的是,在Dart中,每一个语句结束必须加上 ; 以表示语句结束

final 与 const

const 就是JS中的const,声明一个变量(一般是常量),从此不再修改。这里把final也放到这里,当然功效也是一样的

1
2
3
4
5
6
7
8
void main() {
var a = 10;
final b = a;
const c = 10;

b = 2;
c = 4;
}

如果你执行了上面的代码,你将会看到如下的错误

错误提示

final 在声明的时候更多的声明一个变量,而 const 更多的时候声明的是一个常量,有点像JS中的 letconst,例如在Flutter初始化项目的时候有一个这样的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);

// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.

// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".

final String title;

@override
_MyHomePageState createState() => _MyHomePageState();
}

final 与 const的区别

  1. final 要求变量只能初始化一次,并不要求赋的值一定是编译时常量,可以是常量也可以不是。而 const 要求在声明时初始化,并且赋值必需为编译时常量
  2. final 是惰性初始化,即在运行时第一次使用前才初始化。而 const 是在编译时就确定值了。

函数

Dart中的函数有三种

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

// 声明返回值类型 int
int add1(int a, int b){
return a + b;
}

// 不声明返回值类型
add2(int a, int b){
return a + b;
}

// 使用箭头函数
add3(a, b) => a + b;

void main(){
print(add1(1, 2)); // 3
print(add2(1, 1)); // 2
print(add3('hello ', ' world')); // hello world
}

需要注意的是,Dart中函数中参数的类型需要有一定的限制,同时numString是不可相加的

1
2
3
void main(){
print(1+'a'); // Error: The argument type 'String' can't be assigned to the parameter type 'num'. print(1+'a');
}

函数参数

命名参数

命名参数是有两种方式 type key 或者是 key: type 的形式,但是都必须使用 {} 括起来,例如

1
2
3
4
5
person({String name, int age}) => print('Hello, my name is $name, i am $age.');

void main(){
person(name: '踏浪', age: 18);
}

传递参数的时候使用 key: value 的形式即可。

可选位置参数

把一些方法的参数放到 [] 中就变成可选 位置参数了

1
2
3
4
5
6
7
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}

不使用可选参数

1
print(say('Bob', 'Howdy')) // 'Bob says Howdy'

使用可选参数

1
print(say('Bob', 'Howdy', 'smoke signal')) // 'Bob says Howdy with a smoke signal'

参数默认值

你可以给参数指定默认值,如果传递了参数,那么取代默认值,否则使用默认值

1
2
3
4
5
6
person(String name = '踏浪') => 'My name is $name'

void main(){
print(person()); // My name is 踏浪
print(person('Anthony')); // My name is Anthony
}

入口函数

每一个.dart文件都应该有一个入口函数,即:

1
2
3
4
5
6
7
8
void main(){
// do something
}

// 还可以有一个可选参数,参数类型是 List
void main(List<String> arguments){
// do something
}

一等对象方法

可以把方法当做参数调用另外一个方法。例如:

1
2
3
4
5
6
7
8
printElement(element) {
print(element);
}

var list = [1, 2, 3];

// Pass printElement as a parameter.
list.forEach(printElement);

匿名函数

同样是上面的例子

1
2
3
4
5
6
var list = [1, 2, 3];

// Pass printElement as a parameter.
list.forEach((element) {
print(element);
});

forEach方法中的参数函数就是一个匿名函数

函数返回值

所有的函数都有返回值,如果没有指定return语句,那么该函数的返回值为null。

操作符

Dart中的操作符在大部分语言中都是有的,可以看看下面的表格
|描述|操作符|
|—|—|
|unary postfix| expr++ expr– () [] . ?.|
|unary prefix| -expr !expr ~expr ++expr –expr |
|multiplicative| / % ~/|
|additive| + -|
|shift| << >>|
|bitwise AND| &|
|bitwise XOR| ^|
|bitwise OR ||
|relational and type test| >= > <= < as is is!|
|equality| == != |
|logical AND| &&|
|logical OR| || |
|if null| ??|
|conditional| expr1 ? expr2 : expr3|
|cascade| ..|
|assignment| =
= /= ~/= %= += -= <<= >>= &= ^= |= ??=|

常见的就可以不用说了,主要说一下在JS中不怎么常见的。

type tset(类型判断)

  • as:类型转换
  • is:类型判断,如果对象是指定的类型怎返回 true
    = is!:类型转换,与is相反
1
2
3
4
5
6
if (emp is Person) { // 类型检测
emp.firstName = 'Bob';
}

// 可以使用以下代码简化上面的代码
(emp as Person).firstName = 'Bob';

注意: 上面这两个代码效果是有区别的。如果 emp 是 null 或者不是 Person 类型, 则第一个示例使用 is 则不会执行条件里面的代码,而第二个情况使用 as 则会抛出一个异常。

赋值操作

常用的赋值操作符都是=,dart中有一个??=

1
2
3
a = value;   // 给 a 变量赋值
b ??= value; // 如果 b 是 null,则赋值给 b;
// 如果不是 null,则 b 的值保持不变

级联操作符(cascade) (..)

级联操作有点像JQ中的链式调用,不同点是JQ的链式调用需要上一个方法返回这个对象自身,而级联操作是不需要的,使用级联操作后,自动返回自身

如果是在web前端中,我们要获取一个元素,修改他的一些属性

1
2
3
4
5
var img = document.querySelector('img')
img.alt = '图片'
img.src = 'XXX'
img.width = '100'
img.height = '100'

如果是使用Dart中的..操作符,可以这样写(这里只是以此为例,不一定存在前端中的document等)

1
2
3
4
5
document.querySelector('img')
..alt = '图片'
..src = 'XXX'
..width = '100'
..height = '100'

流程控制

Dart中的流程控制与前端中的流程控制是一样的,也就以下几种

  • if and if...else
  • for loops
  • while and do-while
  • break and continue
  • switch and case
  • try...catch and try...finally
  • assert

还是有一些不同点的,需要注意

  1. switch...case 语句中,case语句中的数据类型必须是跟switch中的类型一致

Dart中还有一个assert(断言),作用是:如果条件表达式结果不满足需要,则可以使用 assert 语句俩打断代码的执行。

1
2
3
4
5
// 确保 text 不是nunll
assert(text != null);

// 确保 number 的值 小于100
assert(number < 100);

但是:断言只在开发环境有效,如果是生产环境,则无效

class 类

前端在ES6中才引入了类的概念,即使用class关键字创建一个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Point {
num x, y = 10;
num z = 0;

Point(num x, num y) {
this.x = x;
this.y = y;
}
}

void main(){
var p = new Point(1, 3);
print(p.x); // 1
print(p.y); // 3
print(p.z); // 0
}

在使用class构建一个类的同时,在定义一个与类名字相同的方法就定义了一个构造函数,如上面的Point类中的Point方法。

由于把构造函数参数赋值给实例变量的场景太常见了, Dart 提供了一个语法糖来简化这个操作:

如下面的代码和之前的代码是完全一样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Point {
num x, y = 10;
num z = 0;

// 默认的构造方法
Point(this.x, this.y);
}

void main(){
var p = new Point(1, 3);
print(p.x); // 1
print(p.y); // 3
print(p.z); // 0
}

类的继承

如果你是用过react进行开发前端项目,那么你一定对class ... extends ...非常的属性,不错,Dart中也是使用extends继承的

1
2
3
4
5
6
7
8
9
10
11
12
class Human {
String name;
Human.fromJson(Map data) {
print("Human's fromJson constructor");
}
}

class Man extends Human {
Man.fromJson(Map data) : super.fromJson(data) {
print("Man's fromJson constructor");
}
}

由于Human类没有默认构造方法,只有一个命名构造方法 fromJson ,所以在Man类继承Human类时,需要调用父类的fromJson方法做初始化,而且必须使用Man.fromJson(Map data) : super.fromJson(data)这种写法

重定向构造函数

有时候一个构造函数会调动类中的其他构造函数。 一个重定向构造函数是没有代码的,在构造函数声明后,使用 冒号 调用其他构造函数。

1
2
3
4
5
6
7
8
9
10
class Point {
num x;
num y;

// The main constructor for this class.
Point(this.x, this.y);

// Delegates to the main constructor.
Point.alongXAxis(num x) : this(x, 0);
}

getters and setters

getterssetters 是用来设置和访问对象属性的特殊 函数。每个实例变量都隐含的具有一个 getter, 如果变量不是 final 的则还有一个 setter。 你可以通过实行 gettersetter 来创建新的属性, 使用 getset 关键字定义 gettersetter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Rectangle {
num left;
num top;
num width;
num height;

Rectangle(this.left, this.top, this.width, this.height);

// Define two calculated properties: right and bottom.
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}

main() {
var rect = new Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;
assert(rect.left == -8);
}

抽象函数(抽象类)

定义一个抽象类需要使用 abstract 关键字,然后在定义一个方法,类型是 void ,具体是实现,由子类实现

1
2
3
4
5
6
7
8
9
10
11
abstract class Doer {
// ...Define instance variables and methods...

void doSomething(); // Define an abstract method.
}

class EffectiveDoer extends Doer {
void doSomething() {
// ...Provide an implementation, so the method is not abstract here...
}
}

调用一个没实现的抽象函数会导致运行时异常。

运算符重载

如果你定义了一个 Vector 类, 你可以定义一个 + 函数来实现两个向量相加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Vector {
final int x;
final int y;
const Vector(this.x, this.y);

/// Overrides + (a + b).
Vector operator +(Vector v) => new Vector(x + v.x, y + v.y);

/// Overrides - (a - b).
Vector operator -(Vector v) => new Vector(x - v.x, y - v.y);

printRes(){
print('X: $x , Y: $y ');
}
}

main() {
final v = new Vector(2, 3);
final w = new Vector(2, 2);
(v + w).printRes(); // X: 4 (2+2) , Y: 5(3+2)
(v - w).printRes(); // X: 0 (2-2) , Y: 1(3-2)
}

枚举类

枚举类型通常称之为 enumerations 或者 enums, 是一种特殊的类,用来表现一个固定数目的常量。使用enum关键字定义

1
2
3
4
5
enum Color {
red,
green,
blue
}

枚举类型中的每个值都有一个 index getter 函数, 该函数返回该值在枚举类型定义中的位置(从 0 开始),有点像数组的索引。

枚举类型具有如下的限制:

  • 无法继承枚举类型、无法使用 mixin 、无法实现一个枚举类型
  • 无法显示的初始化一个枚举类型

mixins

Mixins 是一种在多类继承中重用 一个类代码的方法。

使用 with 关键字后面为一个或者多个 mixin 名字来使用 mixin。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A {
a() {
print("A's a()");
}
}

class B {
b() {
print("B's b()");
}
}

// 使用with关键字,表示类C是由类A和类B混合而构成
class C = A with B;

main() {
C c = new C();
c.a(); // A's a()
c.b(); // B's b()
}

静态函数与变量

使用 static 关键字来实现类级别的变量和函数。

1
2
3
4
5
6
7
8
9
10
class Person {
static String name = '踏浪';
static sayName() {
print('我的名字是$name');
}
}
void main(){
print(Person.name);
Person.sayName();
}

可以看到,静态函数与变量我们可以直接调用,而不需要通过 new 实现实例后在进行处理。

Generics(泛型)

1
var names = new List<String>();

上面的代码中,<...>表示的就是这个List中每一项的类型,上面的代码中是String,表示的就是这个List中的每一项都要是String类型,而不能是其他的类型。

泛型是什么呢?泛型就是这一个对象中的内容可以使任何的类型,通常情况下,在<>中使用一个字母来代表类型参数, 例如 E, T, S, K, 和 V 等。例如<E>

使用泛型的好处:

  • 正确指定泛型类型会产生更好的生成代码。
  • 泛型可以减小代码的复杂度

更多的泛型知识可以点击这里查看

Dart库

使用 import 来指定一个库如何使用另外 一个库。

1
2
import 'dart:html'; // 导入 web 组件库
import 'dart:math'; // 导入 数学计算库

更多官方库可以点击查看

我们也可以引用自己的.dart文件,类似于 ES6 中的 import

1
2
// add.dart
add(int a, int b) => a + b;

在另一个文件引入

1
2
3
4
5
import './add.dart';

void main(){
print(add(1,2));
}

使用 as 创建别名

1
2
3
4
5
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// ...
Element element1 = new Element(); // Uses Element from lib1.
lib2.Element element2 = new lib2.Element(); // Uses Element from lib2.

使用 show and hide 过滤引入内容

1
2
3
4
5
// Import only foo.
import 'package:lib1/lib1.dart' show foo;

// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;

使用 deferred as 来延时载入库

1
import 'package:deferred/hello.dart' deferred as hello;

异步支持

Dart支持ES7的 async await 方法

下面的代码使用Dart从网络获取数据并打印出来

1
2
3
4
5
6
7
8
9
10
11
12
13
import 'dart:async';
import 'package:http/http.dart' as http;

Future<String> getNetData() async{
http.Response res = await http.get("http://www.baidu.com");
return res.body;
}

main() {
getNetData().then((str) {
print(str);
});
}

以上就是关于Dart语法的简单介绍,想要了解更多,可以参阅Dart官网

注:参考文献

  1. https://www.jianshu.com/p/06aebcad0543
  2. Dart中文
  3. Dart官网
文章作者: 踏浪
文章链接: https://blog.lyt007.cn/技术/Flutter-Lesson-2:Dart-语法简单介绍.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 踏浪 - 前端技术分享
支付宝
微信打赏