Dart语法【Dart中文】可以说是Google因为为Flutter量身定做的一门语言,所以,我们之前基本上都没有接触过这门语言,在入手Flutter坑的时候,还必须了解一下Dart的语言特性。
Dart官网【Dart中文】上面已经有了很详细的说明,我这里只是见多的说说,提取一些重点,以便自己复习以及你们学习。 v
每一个.Dart
文件都会有一个mian()
函数,这个称之为程序的入口文件,这就像是前端ES6语法中的export
一样,都要有一个输出。其实,Dart的设计就有一点遵循前端的ES6语法,不过他还结合了一些c语言
// 程序的入口文件
main() {
/* 或者用这种注释 */
// main函数输出了一点东西,print 控制台输出
print('我是main函数,我是程序执行的入口');
}
代码都可以在DartPad上面运行,你也可以自行修改代码,不过不可能需要开着V**才可以访问这个网站。
关键字的意思就不用多说了,注意上图中的关键字,在变量命名的时候不要使用就行了。
_
开头,有点像JS中我们自定义函数的时候默认_
开头的为私有变量$
,这里面的$
一般用于字符串拼接
每一种编程语言都会有变量,Dart也不例外,不过Dart的区别就是其声明变量的方式有多种。
最简单的就是使用var
关键字,你也可以使用dynamic
,表示没有指定变量类型
var name = 'tal';
dynamic number = 0.5;
使用var
定义变量,最好是定义局部变量
如果你在定义了一个变量以后,同时并没有对其进行赋值,那么,这个变量最终的类型会是null
(注意不是undefined
)。
因为前面我已以及说过了,Dart中所有的变量引用都是对象。
在声明变量的时候我们可以加上他的类型,这个是google从TS中吸取到的经验
int number = 1;
String name = 'tal';
使用这种方式定义变量可以是全局变量和局部变量,但是如果是使用var
定义变量,最好是用于定义局部变量。
Dart中内置了以下几种类型
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中,每一个语句结束必须加上 ; 以表示语句结束
const
就是JS中的const,声明一个变量(一般是常量),从此不再修改。这里把final
也放到这里,当然功效也是一样的
void main() {
var a = 10;
final b = a;
const c = 10;
b = 2;
c = 4;
}
如果你执行了上面的代码,你将会看到如下的错误
final
在声明的时候更多的声明一个变量,而 const
更多的时候声明的是一个常量,有点像JS中的 let
与 const
,例如在Flutter初始化项目的时候有一个这样的代码。
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
要求在声明时初始化,并且赋值必需为编译时常量final
是惰性初始化,即在运行时第一次使用前才初始化。而 const
是在编译时就确定值了。
Dart中的函数有三种
// 声明返回值类型 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
是不可相加的
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
的形式,但是都必须使用 {} 括起来,例如
person({String name, int age}) => print('Hello, my name is $name, i am $age.');
void main(){
person(name: '踏浪', age: 18);
}
传递参数的时候使用 key: value
的形式即可。
把一些方法的参数放到 []
中就变成可选 位置参数了
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
不使用可选参数
print(say('Bob', 'Howdy')) // 'Bob says Howdy'
使用可选参数
print(say('Bob', 'Howdy', 'smoke signal')) // 'Bob says Howdy with a smoke signal'
你可以给参数指定默认值,如果传递了参数,那么取代默认值,否则使用默认值
person(String name = '踏浪') => 'My name is $name'
void main(){
print(person()); // My name is 踏浪
print(person('Anthony')); // My name is Anthony
}
每一个.dart
文件都应该有一个入口函数,即:
void main(){
// do something
}
// 还可以有一个可选参数,参数类型是 List
void main(List<String> arguments){
// do something
}
可以把方法当做参数调用另外一个方法。例如:
printElement(element) {
print(element);
}
var list = [1, 2, 3];
// Pass printElement as a parameter.
list.forEach(printElement);
同样是上面的例子
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中不怎么常见的。
true
= is!:类型转换,与is
相反if (emp is Person) { // 类型检测
emp.firstName = 'Bob';
}
// 可以使用以下代码简化上面的代码
(emp as Person).firstName = 'Bob';
注意: 上面这两个代码效果是有区别的。如果 emp 是 null 或者不是 Person 类型, 则第一个示例使用 is 则不会执行条件里面的代码,而第二个情况使用 as 则会抛出一个异常。
常用的赋值操作符都是=
,dart中有一个??=
a = value; // 给 a 变量赋值
b ??= value; // 如果 b 是 null,则赋值给 b;
// 如果不是 null,则 b 的值保持不变
级联操作有点像JQ中的链式调用,不同点是JQ的链式调用需要上一个方法返回这个对象自身,而级联操作是不需要的,使用级联操作后,自动返回自身
如果是在web前端中,我们要获取一个元素,修改他的一些属性
var img = document.querySelector('img')
img.alt = '图片'
img.src = 'XXX'
img.width = '100'
img.height = '100'
如果是使用Dart中的..
操作符,可以这样写(这里只是以此为例,不一定存在前端中的document等)
document.querySelector('img')
..alt = '图片'
..src = 'XXX'
..width = '100'
..height = '100'
Dart中的流程控制与前端中的流程控制是一样的,也就以下几种
if
and if...else
for
loopswhile
and do-while
break
and continue
switch
and case
try...catch
and try...finally
assert
还是有一些不同点的,需要注意
switch...case
语句中,case语句中的数据类型必须是跟switch中的类型一致Dart中还有一个assert(断言),作用是:如果条件表达式结果不满足需要,则可以使用 assert 语句俩打断代码的执行。
// 确保 text 不是nunll
assert(text != null);
// 确保 number 的值 小于100
assert(number < 100);
但是:断言只在开发环境有效,如果是生产环境,则无效
。
前端在ES6中才引入了类的概念,即使用class
关键字创建一个类
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 提供了一个语法糖来简化这个操作:
如下面的代码和之前的代码是完全一样的
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
继承的
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)
这种写法
有时候一个构造函数会调动类中的其他构造函数。 一个重定向构造函数是没有代码的,在构造函数声明后,使用 冒号
调用其他构造函数。
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
和 setters
是用来设置和访问对象属性的特殊 函数。每个实例变量都隐含的具有一个 getter,
如果变量不是 final
的则还有一个 setter。
你可以通过实行 getter
和 setter
来创建新的属性, 使用 get
和 set
关键字定义 getter
和 setter
:
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
,具体是实现,由子类实现
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
类, 你可以定义一个 +
函数来实现两个向量相加。
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
关键字定义
enum Color {
red,
green,
blue
}
枚举类型中的每个值都有一个 index getter
函数, 该函数返回该值在枚举类型定义中的位置(从 0 开始),有点像数组的索引。
枚举类型具有如下的限制:
mixin
、无法实现一个枚举类型
Mixins
是一种在多类继承中重用 一个类代码的方法。
使用 with
关键字后面为一个或者多个 mixin 名字来使用 mixin。
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
关键字来实现类级别的变量和函数。
class Person {
static String name = '踏浪';
static sayName() {
print('我的名字是$name');
}
}
void main(){
print(Person.name);
Person.sayName();
}
可以看到,静态函数与变量我们可以直接调用,而不需要通过 new
实现实例后在进行处理。
var names = new List<String>();
上面的代码中,<...>
表示的就是这个List中每一项的类型,上面的代码中是String
,表示的就是这个List中的每一项都要是String类型,而不能是其他的类型。
泛型是什么呢?泛型就是这一个对象中的内容可以使任何的类型,通常情况下,在<>
中使用一个字母来代表类型参数, 例如 E, T, S, K, 和 V 等。例如<E>
使用泛型的好处:
更多的泛型知识可以点击这里查看
使用 import
来指定一个库如何使用另外 一个库。
import 'dart:html'; // 导入 web 组件库
import 'dart:math'; // 导入 数学计算库
更多官方库可以点击查看
我们也可以引用自己的.dart
文件,类似于 ES6 中的 import
// add.dart
add(int a, int b) => a + b;
在另一个文件引入
import './add.dart';
void main(){
print(add(1,2));
}
使用 as
创建别名
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
过滤引入内容
// Import only foo.
import 'package:lib1/lib1.dart' show foo;
// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
使用 deferred as
来延时载入库
import 'package:deferred/hello.dart' deferred as hello;
Dart支持ES7的 async await
方法
下面的代码使用Dart从网络获取数据并打印出来
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官网
注:参考文献