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 | var name = 'tal'; |
使用var
定义变量,最好是定义局部变量
默认值
如果你在定义了一个变量以后,同时并没有对其进行赋值,那么,这个变量最终的类型会是null
(注意不是undefined
)。
因为前面我已以及说过了,Dart中所有的变量引用都是对象。
可选类型
在声明变量的时候我们可以加上他的类型,这个是google从TS中吸取到的经验1
2
3int number = 1;
String name = 'tal';
使用这种方式定义变量可以是全局变量和局部变量,但是如果是使用var
定义变量,最好是用于定义局部变量。
Dart中内置了以下几种类型
- Number
- String
- Boolean
- List ( means array )
- Map
- Rune ( 用于在字符串中表示 Unicode 字符 )
- Symbol
1 | void main() { |
需要注意的是,在Dart中,每一个语句结束必须加上 ; 以表示语句结束
final 与 const
const
就是JS中的const,声明一个变量(一般是常量),从此不再修改。这里把final
也放到这里,当然功效也是一样的1
2
3
4
5
6
7
8void main() {
var a = 10;
final b = a;
const c = 10;
b = 2;
c = 4;
}
如果你执行了上面的代码,你将会看到如下的错误
final
在声明的时候更多的声明一个变量,而 const
更多的时候声明的是一个常量,有点像JS中的 let
与 const
,例如在Flutter初始化项目的时候有一个这样的代码。
1 | class MyHomePage extends StatefulWidget { |
final 与 const的区别
final
要求变量只能初始化一次,并不要求赋的值一定是编译时常量,可以是常量也可以不是。而const
要求在声明时初始化,并且赋值必需为编译时常量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中函数中参数的类型需要有一定的限制,同时num
与String
是不可相加的1
2
3void 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
5person({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
7String 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
6person(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
8void main(){
// do something
}
// 还可以有一个可选参数,参数类型是 List
void main(List<String> arguments){
// do something
}
一等对象方法
可以把方法当做参数调用另外一个方法。例如:1
2
3
4
5
6
7
8printElement(element) {
print(element);
}
var list = [1, 2, 3];
// Pass printElement as a parameter.
list.forEach(printElement);
匿名函数
同样是上面的例子1
2
3
4
5
6var 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 | if (emp is Person) { // 类型检测 |
注意: 上面这两个代码效果是有区别的。如果 emp 是 null 或者不是 Person 类型, 则第一个示例使用 is 则不会执行条件里面的代码,而第二个情况使用 as 则会抛出一个异常。
赋值操作
常用的赋值操作符都是=
,dart中有一个??=
1
2
3a = value; // 给 a 变量赋值
b ??= value; // 如果 b 是 null,则赋值给 b;
// 如果不是 null,则 b 的值保持不变
级联操作符(cascade) (..)
级联操作有点像JQ中的链式调用,不同点是JQ的链式调用需要上一个方法返回这个对象自身,而级联操作是不需要的,使用级联操作后,自动返回自身
如果是在web前端中,我们要获取一个元素,修改他的一些属性1
2
3
4
5var img = document.querySelector('img')
img.alt = '图片'
img.src = 'XXX'
img.width = '100'
img.height = '100'
如果是使用Dart中的..
操作符,可以这样写(这里只是以此为例,不一定存在前端中的document等)1
2
3
4
5document.querySelector('img')
..alt = '图片'
..src = 'XXX'
..width = '100'
..height = '100'
流程控制
Dart中的流程控制与前端中的流程控制是一样的,也就以下几种
if
andif...else
for
loopswhile
anddo-while
break
andcontinue
switch
andcase
try...catch
andtry...finally
assert
还是有一些不同点的,需要注意
- 在
switch...case
语句中,case语句中的数据类型必须是跟switch中的类型一致
Dart中还有一个assert(断言),作用是:如果条件表达式结果不满足需要,则可以使用 assert 语句俩打断代码的执行。
1 | // 确保 text 不是nunll |
但是:断言只在开发环境有效,如果是生产环境,则无效
。
class 类
前端在ES6中才引入了类的概念,即使用class
关键字创建一个类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class 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
14class 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 | class Human { |
由于Human类没有默认构造方法,只有一个命名构造方法 fromJson
,所以在Man类继承Human类时,需要调用父类的fromJson方法做初始化,而且必须使用Man.fromJson(Map data) : super.fromJson(data)
这种写法
重定向构造函数
有时候一个构造函数会调动类中的其他构造函数。 一个重定向构造函数是没有代码的,在构造函数声明后,使用 冒号
调用其他构造函数。1
2
3
4
5
6
7
8
9
10class 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
getters
和 setters
是用来设置和访问对象属性的特殊 函数。每个实例变量都隐含的具有一个 getter,
如果变量不是 final
的则还有一个 setter。
你可以通过实行 getter
和 setter
来创建新的属性, 使用 get
和 set
关键字定义 getter
和 setter
:
1 | class Rectangle { |
抽象函数(抽象类)
定义一个抽象类需要使用 abstract
关键字,然后在定义一个方法,类型是 void
,具体是实现,由子类实现1
2
3
4
5
6
7
8
9
10
11abstract 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
22class 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 | enum Color { |
枚举类型中的每个值都有一个 index getter
函数, 该函数返回该值在枚举类型定义中的位置(从 0 开始),有点像数组的索引。
枚举类型具有如下的限制:
- 无法继承枚举类型、无法使用
mixin
、无法实现一个枚举类型 - 无法显示的初始化一个枚举类型
mixins
Mixins
是一种在多类继承中重用 一个类代码的方法。
使用 with
关键字后面为一个或者多个 mixin 名字来使用 mixin。
1 | class A { |
静态函数与变量
使用 static
关键字来实现类级别的变量和函数。
1 | class Person { |
可以看到,静态函数与变量我们可以直接调用,而不需要通过 new
实现实例后在进行处理。
Generics(泛型)
1 | var names = new List<String>(); |
上面的代码中,<...>
表示的就是这个List中每一项的类型,上面的代码中是String
,表示的就是这个List中的每一项都要是String类型,而不能是其他的类型。
泛型是什么呢?泛型就是这一个对象中的内容可以使任何的类型,通常情况下,在<>
中使用一个字母来代表类型参数, 例如 E, T, S, K, 和 V 等。例如<E>
使用泛型的好处:
- 正确指定泛型类型会产生更好的生成代码。
- 泛型可以减小代码的复杂度
更多的泛型知识可以点击这里查看
Dart库
使用 import
来指定一个库如何使用另外 一个库。1
2import 'dart:html'; // 导入 web 组件库
import 'dart:math'; // 导入 数学计算库
更多官方库可以点击查看
我们也可以引用自己的.dart
文件,类似于 ES6 中的 import
1
2// add.dart
add(int a, int b) => a + b;
在另一个文件引入1
2
3
4
5import './add.dart';
void main(){
print(add(1,2));
}
使用 as
创建别名1
2
3
4
5import '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
13import '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官网
注:参考文献