Dart语法基础
Dart语言简介
Dart是Google推出的一门编程语言,最初是希望取代Javascript运行在浏览器端,后台慢慢发展成可以开发Android、iOS和web端APP的一门高质量的编程语言,目前Dart的版本是Dart2。
Dart语言特性
Productive
Dart’s syntax is clear and concise, its tooling simple yet powerful. Sound typing helps you to identify subtle errors early. Dart has battle-hardened core libraries and an ecosystem of thousands of packages.
Fast
Dart provides optimizing ahead-of-time compilation to get predictably high performance and fast startup across mobile devices and the web.
Portable
Dart compiles to ARM and x86 code, so that Dart mobile apps can run natively on iOS, Android, and beyond. For web apps, Dart transpiles to JavaScript.
Approachable
Dart is familiar to many existing developers, thanks to its unsurprising object orientation and syntax. If you already know C++, C#, or Java, you can be productive with Dart in just a few days.
Reactive
Dart is well-suited to reactive programming, with support for managing short-lived objects—such as UI widgets—through Dart’s fast object allocation and generational garbage collector. Dart supports asynchronous programming through language features and APIs that use Future and Stream objects.
Dart语法简介
官网上对于Dart的语法也有详细介绍,不过是全英文的,如果对英文没有什么阅读障碍,可以直接移步官方文档。
下面我们通过Android Studio作为开发工具来一起了解Dart的语法基础。
新创建的Flutter项目,Dart代码主要在 lib/main.dart
文件中,由于本篇主要讲的是Dart的语法,故暂时不看main.dart文件,在lib目录下我们创建一个新的.dart
文件grammar.dart
,如图:
然后我们在grammar.dart
中键入以下代码
// Define a function.
printInteger(int aNumber) {
print('The number is $aNumber.'); // Print to console.
}
// This is where the app starts executing.
main() {
var number = 42; // Declare and initialize a variable.
printInteger(number); // Call a function.
}
这段代码一些基本的用法,基本所有的语言都通用的语法:
// This is a comment.
单行代码注释,不用多说
int
int是Dart中的一种数据类型,同时还有其他的数据类型如:String List bool等。
42
一个数字字面量,数字字面量是编译时常量的一种。
print()
显示输出的一种便捷方法
'xxx' 或者 “xxx”
两种方式都可以表示字符串
$variableName 或者 ${expression}
字符串插值:包含一个变量或表达式的字符串等效于字符串字面量
sample:
var name = 'zgy';
print("hi,I am $name");
int a = 1,b = 2;
print("$a + $b = ${a + b}");
main()
主函数,程序从这里开始。。。
var
一种声明变量而不指定其类型的方法
示例代码中 var number = 42; //这里42被推断为int类型
Dart重要概念
一切皆对象,无论数字、函数、和null都是对象。所有对象都继承自[Object]类。
Dart是强类型语言,但是类型声明可选,因为Dart可以推断类型。(类似Swift)
Dart支持通用类型,如List
(整数列表)或List (任何类型的对象列表)。 Dart支持顶级函数(如main()),以及绑定到类或对象(分别是静态方法(static)和实例(instance)方法)的函数。您还可以在函数(嵌套或局部函数)中创建函数。
类似地,Dart支持顶级变量,以及绑定到类或对象(静态和实例变量)的变量。实例变量有时被称为字段或属性。
与Java不同,Dart没有公开、保护和私有的关键字。如果标识符以下划线(_)开头,则该标识符对其库是私有的。有关详细信息,请参见[库和可见性]。
标识符可以以字母或下划线(_)开头,然后是这些字符加上数字的任何组合。
有时候,某事物是一个表达(expression )还是一个语句(statement)是很重要的,所以这两个词要准确。
Dart工具可以报告两种问题:警告和错误。警告只是表明您的代码可能不工作,但它们不会阻止您的程序执行。错误可以是编译时错误,也可以是运行时错误。编译时错误阻止了代码的执行;运行时错误导致代码执行时引发异常。
Dart变量
声明变量有多种方式:
main(){
int a = 10; //指明变量类型为int
bool = true; //指明变量类型为bool
String name = 'zgy'; //指明变量类型为String
var name = 'zgy'; //自动推断变量类型String
dynamic name = 'zgy'; //自动推断变量类型String
}
默认值
未初始化的变量的初始值为null
。
Final 和 const修饰符
如果您从未打算更改一个变量,请使用final或const修饰他,而不是使用var或其他变量类型。最终变量只能设置一次;const变量是一个编译时常数。(Const变量是隐式最终变量。)最终的顶级或类变量在第一次使用时被初始化。
注意:实例变量可以是final,但不能是const。[实例变量定义在对象一级,它可以被类中的任何方法或者其他类中的方法访问,但是不能被静态方法访问。]
Sample:
final name = 'zgy'; // Without a type annotation
final String nickname = 'zgy';
你无法更改final变量的值:
name = 'zgy'; //Error:a final variable can only be set once.
//这是个错误的示例,使用final声明的变量是不可以更改的。
对于想要在编译时确定并且不再变的变量,使用const。如果const变量位于类级别,则将其标记为静态const。在声明该变量时,将该值设置为编译时常量,例如数字或字符串字面量、const变量或常量数字算术运算的结果:
const bar = 1000000; // Unit of pressure (dynes/cm2)
const double atm = 1.01325 * bar; // Standard atmosphere
const关键字不只是声明常量变量。您还可以使用它来创建常量值,以及声明创建常量值的构造函数。任何变量都可以赋一个常量值。
var foo = const [];
final bar = const [];
const baz = []; // Equivalent to `const []`
您可以从const声明的初始化表达式中省略const,如上面的baz。
您可以更改一个非final的非const变量的值,即使它曾经有一个const值:
foo = [1, 2, 3]; // Was const []
你不能改变const变量的值:
baz = [42]; // Error: Constant variables can't be assigned a value.
Final和Const的区别:
- 区别一:
final
要求变量只能初始化一次,并不要求赋的值一定是编译时常量,可以是常量也可以不是。而const
要求在声明时初始化,并且赋值必需为编译时常量。 - 区别二:
final
是惰性初始化,即在运行时第一次使用前才初始化。而const
是在编译时就确定值了。
内建类型
Dart有以下几种内建的数据类型:
- numbers
- strings
- booleans
- lists (also known as arrays)
- maps
- runes (for expressing Unicode characters in a string)
- symbols
下面用一段代码来演示以上各类数据类型:
main() {
// numbers 有两种形式 int和double
var a = 0;
int b = 1;
double c = 0.1;
// strings
var s1 = 'zgy';
String s2 = "zgy";
// booleans
var real = true;
bool isReal = false;
// lists
var arr = [1, 2, 3, 4, 5];
List arr2 = ['hello', 'world', "123", "456"];
List arr3 = [1, true, 'zgy', 1.0];
// maps 在Dart2中new关键字是可选的
var map = new Map();
map['name'] = 'zhangsan';
map['age'] = 10;
Map m = new Map();
m['a'] = 'a';
//runes,Dart 中 使用runes 来获取UTF-32字符集的字符。String的 codeUnitAt and codeUnit属性可以获取UTF-16字符集的字符
var clapping = '\u{1f44f}';
print(clapping); // 打印的是拍手emoji的表情
// symbols 符号对象表示在Dart程序中声明的操作符或标识符。您可能永远不需要使用符号,但是对于按名称引用标识符的api来说,它们是非常重要的,因为缩小改变了标识符名称而不是标识符符号
print(#s == new Symbol("s")); // true
}
函数
Dart是一种真正的面向对象语言,所以即使函数也是对象,具有类型和功能。这意味着函数可以分配给变量或作为参数传递给其他函数。您还可以像调用函数一样调用Dart类的实例。
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
//或
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
//以上两个函数等价,省略类型函数仍然可以工作
对于只包含一个表达式的函数,可以使用简写语法:
// =>可以替代return
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
可选参数
可选的命名参数
在定义函数时,使用{param1, param2,…}来指定命名参数:
/// Sets the [bold] and [hidden] flags ...
//注意花括号
void enableFlags({bool bold, bool hidden}) {...}
在调用函数时,可以使用paramName: value来指定命名参数。例如:
enableFlags(bold: true, hidden: false);
可以使用@required说明它是一个必传的参数:
const Scrollbar({Key key, @required Widget child})
可选位置参数
在[]中包装一组函数参数,标记为可选的位置参数:
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
main(){
print(say('Bob', 'Howdy'));
//log: ob says Howdy
print(say('Bob', 'Howdy', 'smoke signal'));
//log: Bob says Howdy with a smoke signal
}
默认参数值
函数可以使用=
来定义命名和位置参数的默认值。如果没有提供默认值,则默认值为null。
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold = false, bool hidden = false}) {...}
// bold will be true; hidden will be false.
enableFlags(bold: true);
匿名函数
大多数函数都被命名,如main()或printElement()。你也可以创建一个没有函数名称的函数,这种函数称为匿名函数。
test(Function callback) {
callback('I am zgy');
}
main(){
test((String name){
print('$name');
});
}
运算符
描述 | 操作符 |
---|---|
一元后置操作符 | expr++ expr-- () [] . ?. |
一元前置操作符 | -expr !expr ~expr ++expr --expr |
乘除运算 | * / % ~/ |
加减运算 | + - |
移位运算 | << >> |
按位与 | & |
按位异或 | ^ |
按位或 | | |
关系和类型测试 | >= > <= < as is is! |
相等 | == != |
逻辑与 | && |
逻辑或 | || |
是否为null | ?? |
天健判断(三元运算) | expr1 ? expr2 : expr3 |
级联 | .. |
赋值 | = *= /= ~/= %= += -= <<= >>= &= ^= |
main() {
// 与Java相同的运算符操作
int a = 1;
++a;
a++;
var b = 1;
print(a == b); // false
print(a * b); // 3
bool real = false;
real ? print('real') : print('not real'); // not real
print(real && a == b); // false
print(real || a == 3); // true
print(a != 2); // true
print(a <= b); // false
var c = 9;
c += 10;
print("c = $c"); // c = 19
print(1<<2); // 4
// 与Java不太一样的运算符操作
// is运算符用于判断一个变量是不是某个类型的数据
// is!则是判断变量不是某个类型的数据
var s = "hello";
print(s is String); // true
var num = 6;
print(num is! String); // true
// ~/才是取整运算符,如果使用/则是除法运算,不取整
int k = 1;
int j = 2;
print(k / j); // 0.5
print(k ~/ j); // 0
// as运算符类似于Java中的cast操作,将一个对象强制类型转换
(emp as Person).teach();
// ??=运算符 如果 ??= 运算符前面的变量为null,则赋值,否则不赋值
var param1 = "hello", param2 = null;
param1 ??= "world";
param2 ??= "world";
print("param1 = $param1"); // param1 = hello
print("param2 = $param2"); // param2 = world
// ?.运算符
var str1 = "hello world";
var str2 = null;
print(str1?.length); // 11
print(str2?.length); // null
print(str2.length); // 报错
}
..运算符(级联操作)
class Person {
eat() {
print("I am eating...");
}
sleep() {
print("I am sleeping...");
}
study() {
print("I am studying...");
}
}
main() {
// 依次打印
// I am eating...
// I am sleeping...
// I am studying...
new Person()..eat()
..sleep()
..study();
}
可以看出..
调用某个对象的方法(或者成员变量)时,返回值是这个对象的本身,所以你可以接着使用用..
调用这个对象的其他方法。
流程控制语句
您可以使用以下任何一种方法来控制Dart代码的流:
- if 和 else
- for循环
- while和do-while循环
- break和continue
- switch和case
- assert
你也可以使用try-catch和throw来对控制流程作出改变。
类
Dart是一种面向对象的语言,具有类和基于mixin的继承。每个对象都是一个类的实例,所有的类都是Object的子类。基于mixin的继承意味着,尽管每个类(除了Object)都只有一个超类,但类主体可以在多个类层次结构中重用。
Dart中的类没有访问控制,所以你不需要使用private
、protected
、public
等修饰成员变量或成员函数,一个简单的类如下代码所示:
class Person {
String name;
int age;
String gender;
//类同名的构造方法
Person(this.name, this.age, this.gender);
//Person.init(){ 类的命名构造方法
// name = 'baby';
// age = 1;
//}
sayHello() {
print("hello,my name is $name,I am $age years old, I am $gender");
}
}
上面的Person
类中有3个成员变量,一个构造方法和一个成员方法,看起来比较奇怪的是Person
的构造方法,里面传入的3个参数都是this.xxx
,而且没有大括号{}
包裹的方法体,这种语法是Dart比较独特而简洁的构造方法声明方式,它等同于下面的代码:
Person(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
使用类成员
使用点号(.)引用实例变量或方法:
main (){
var p = Person("zhang", 10,"male");
p.sayHello();
p.age = 20;
p.name = 'lisi';
p.sayHello();
}
为避免最左操作数为空时出现异常,使用 ?.代替 .来使用:
// If p is non-null, set its y value to 4.
p?.y = 4;
类的继承
Dart
中使用extends
关键字做类的继承,如果一个类只有命名的构造方法,在继承时需要注意,如下代码:
class Student extents Person{
Student.init() : super.init(){
//
}
}
类的成员方法
一个类的成员方法是一个函数,为这个类提供某些行为。上面的代码中已经有了一些类的成员方法的定义,这些定义方式跟Java
很类似,你可以为某个类的成员变量提供getter/setter
方法,如下代码:
class Rectangle {
num left, top, width, height;
// 构造方法传入left, top, width, height几个参数
Rectangle(this.left, this.top, this.width, this.height);
// right, bottom两个成员变量提供getter/setter方法
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
抽象类和抽象方法
使用abstract修饰一个类,则这个类是抽象类,抽象类中可以有抽象方法和非抽象方法,抽象方法没有方法体,需要子类去实现,如下代码:
abstract class Doer {
// 抽象方法,没有方法体,需要子类去实现
void doSomething();
// 普通的方法
void greet() {
print("hello world!");
}
}
class EffectiveDoer extends Doer {
// 实现了父类的抽象方法
void doSomething() {
print("I'm doing something...");
}
}
枚举类
使用 enum
关键字定义一个枚举类
enum frame {x, y, width, height}
mixins
mixins是一个重复使用类中代码的方式,比如下面的代码:
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
关键字修饰类的成员变量和成员方法
泛型
泛型通常是类型安全所必需的,他们对于写出严谨高质量的代码是很有用的:
适当地指定泛型类型可以生成更好的代码。
var names = List
(); names.addAll(['Seth', 'Kathy', 'Lars']); names.add(42); // Error 您可以使用泛型来减少代码重复。
//T是替代类型。它是一个占位符 abstract class Cache
{ T getByKey(String key); void setByKey(String key, T value); }
库
使用库
使用import来指定如何在另一个库的范围中使用来自一个库的命名空间。例如,Dart web应用程序通常使用Dart:html库,它们可以这样导入:
import 'dart:html';
导入一个库仅仅需要提供库的URI。对于内置库,URI具有特定的形式(dart:scheme)。对于其他库,可以使用文件路径或者包:scheme的形式。包:scheme形式指定包管理器(如pub工具)提供的库。例如:
import 'package:test/test.dart';
注意:URI表示统一资源标识符。url(统一资源定位器)是一种常见的URI
指定一个库前缀
如果您导入两个具有冲突标识符的库,那么您可以为一个或两个库指定一个前缀。例如,如果library1和library2都有一个Element类,那么你可以用以下的方法:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// Uses Element from lib1.
Element element1 = Element();
// Uses Element from lib2.
lib2.Element element2 = lib2.Element();
只导入库的一部分
// Import only foo.
import 'package:lib1/lib1.dart' show foo;
// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
懒加载库
延迟加载(也称为懒加载)允许应用程序在需要时按需加载库。以下是一些您可能使用延迟加载的情况:
- 减少应用程序的初始启动时间。
- 例如,要执行A/B测试——尝试算法的其他实现。
- 加载很少使用的功能,如可选屏幕和对话框。
要延迟加载库,必须首先使用deferred as进行导入。
import 'package:greetings/hello.dart' deferred as hello;
当您需要库时,使用库的标识符调用loadLibrary()。
Future greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
部分源码参考:yubo_725和Dart2中文文档