语法学习笔记。资料参考:官方文档(英文)
(要直接看英文文档,中文文档可能是机器翻译的,很多地方语句不通顺,埋坑无数)
语言的特性
- 所有能放进变量中的都是对象,所有对象都是类的实例,所有对象都继承于
Object
。包含数字,方法,null等等。也由此可以知道:没有初始化的变量都是null;没有装箱拆箱的问题。 - Dart是强类型语言。你可以显示声明类型,也可以直接使用
var
,Dart会自动推断类型。当你要声明一个不确定类型的变量时,可以使用特殊类型dynamic
来声明。 - 支持顶级方法,类方法,嵌套方法(在方法中声明方法)
- 没有其他语言的public等关键字,但如果一个标识符以 "_" 开头,则为私有。
-
new
关键字是可选的。
内置类型
numbers:带小数点的就是double,不带的就是int
-
Strings:
1.创建既可以用""
也可以用''
;
2.编码是UTF-16;
3.可以使用"${xxx}"
来格式化输出字符串,其中的{}
一般情况下可以省略,eg."a = $a"
"a = ${a}."
"a = ${a}ah"
,其中最后一个需要加{}
是因为如果不加{}
,相当于"a = $aah"
,使用的值是aah
,而不是a
;- 多行字符串
''' xxxx '''
或者""" xxx """
-
r'\n'
输出的是\n
。其中r
取消字符串中的转义字符。
- 多行字符串
Booleans:不允许使用
if(0)
之类的代码,会编译报错。-
Lists:
- 索引从0开始
- 使用
const
创建的list,该变list中的单个元素会运行报错。
var list = const[1, 2, 3, 4]; list[0] = 12;//这里报错
- 可以在list创建时使用
if
和for
。bool isAddExtraElement = false; var list = [ 0, 1, if(isAddExtraElement) 2 ]; print(list.toString()); var list2 = [ for(int i=0; i < list.length; i++) "forElement:${list[i]}" ]; print(list2);
-
Sets:无序列表
- 创建
//var test = {}; //这样声明的是map,不是set var test = <String>{}; var test1 = {"a", "b", "c", "d", "e"}; var test2 = <String>{}; test2.add("f"); test2.addAll(test1);
- 一样可以用
const
进行创建,使用const
创建的set不能更改。
- 创建
-
Maps:字典
- 创建
var map1 = { 1: 15, 2: 16, 3: 17, 4: 18 }; map1[5] = 19; var map2 = Map(); map2["q"] = 1; map2["w"] = 2; map2["e"] = 3;
- 可以使用
const
进行创建,使用const
创建的Map不能更改。
- 创建
Runes:使用4位16进制数可以输出一些特殊字符。但一般好像没什么卵用。
Runes input = new Runes(
'\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}');
print(new String.fromCharCodes(input));
输出: ♥ 😅 😎 👻 🖖 👍
- Symbols:不清楚这个东西是干嘛的,好像也没什么卵用。
函数
- 函数的返回值类型可以省略,下面两个函数是一样的。
add(num a, num b) {
return a + b;
}
num add(num a, num b) {
return a + b;
}
- 不定参数
```
add(num a, num b, [num c = 0, num d]) {//[]中是不定参数,其中c带默认值
return a + b + c + (d ?? 0);
}
sub({num a = 0, num b = 0, num c}){//{}中是不定参数,其中a, b带默认值
return a - b - (c ?? 0);
}
main(List<String> arguments) {
print(add(1, 2, 3));//和其他语言使用方法一致
print(sub(b : 2, a : 1));//传入参数是按照key值对应的,不用考虑顺序。&&个人认为这种方式使用有些麻烦。
}
```
- 函数能够直接当做变量进行传递,类似于C#中的Action,但不用额外进行变量的定义。
- 匿名函数,使用lamda表达式,语法与C#基本一致。
- 值的一看的官方闭包的例子,用函数创建函数。
/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(num addBy) {
return (num i) => addBy + i;
}
void main() {
// Create a function that adds 2.
var add2 = makeAdder(2);
// Create a function that adds 4.
var add4 = makeAdder(4);
assert(add2(3) == 5);
assert(add4(3) == 7);
}
关于++i
和i++
和其他语言的一样。
这里只是简单记录一下,顺便复习,&&知道这么个东西就行了,开发中不要用,不差多一行代码。
话说谁要是在写代码的时候抖这个机灵,不出bug一切安好,一旦出bug可能让你查到天荒地老。
官方例子
var a, b;
a = 0;
b = ++a; // Increment a before b gets its value.
assert(a == b); // 1 == 1
a = 0;
b = a++; // Increment a AFTER b gets its value.
assert(a != b); // 1 != 0
a = 0;
b = --a; // Decrement a before b gets its value.
assert(a == b); // -1 == -1
a = 0;
b = a--; // Decrement a AFTER b gets its value.
assert(a != b); // -1 != 0
总之:a
靠近=
==> 先赋值
流程控制
if(xxx){} else if(xxx) {} else{}
-
for(int i = 0; i < xxx.Length; i++)
&&for(var a in xxx)
。for ... in
的用法基本等同于C#中的foreach
-
while(xxx){}
&&do{}while{xxx}
-
switch case
基本用法与C#一样,但有一个额外的用法
var command = 'CLOSED';
switch (command) {
case 'CLOSED':
executeClosed();
continue nowClosed;//强制继续执行下面的case。个别地方可能会省一些代码。
// Continues executing at the nowClosed label.
nowClosed:
case 'NOW_CLOSED':
// Runs for both CLOSED and NOW_CLOSED.
executeNowClosed();
break;
}
-
assert
false时中断程序,抛出异常。
抛出异常
throw 'something error !!!'
,可以直接写异常信息。(当然也可以指定异常类型)
异常捕获
try...catch...finally
用法基本与C#的相同,但个别的语法不一样
try {
breedMoreLlamas();
} on OutOfLlamasException {
// A specific exception
buyMoreLlamas();
} on Exception catch (e) {
// Anything else that is an exception
print('Unknown exception: $e');
} catch (e, s) {
// No specified type, handles all
print('Something really unknown: $e $s');
} finally {
// Always clean up, even if an exception is thrown.
cleanLlamaStalls();
}
类
一般类的定义和使用和C#基本一致。需要注意下没有public等关键字就好了。下面记一下有区别的地方。
- 构造函数
- 纯类名构造函数只允许有一个。默认为
ClassName()
。父类额外定义的构造函数,子类至少要继承声明一个。 - 特有的构造函数声明方法
class Point { num x, y; Point(this.x, this.y);//省略赋值。这种方式的构造函数只能有一个。 Point.origin() {//命名构造函数,使用时调用Point.origin()即可。多个构造函数可以用此定义。 x = 0; y = 0; } }
- 构造函数不能自动继承,需要手动实现。
class Animal { num age; Animal(this.age);//定义初始构造函数,唯一 Animal.normal() : this(0);//使用默认构造函数 Animal.dead() { age = 9999; } } class Dog extends Animal { Dog(num age) : super(age);//至少实现一个 }
-
initializer list:冒号后面的部分。构造时优先执行。可以在这里进行一些数据的初始处理或者判断。多条使用
,
隔开。
class Point{ num x; num y; Point(num x, num y) : assert(x != 0 || y != 0) {//数据错误检查 this.x = x; this.y = y; } Point.original() : x = 0, y = 0 {//数据初始值赋值 print("create default point"); } }
- 创建对象时,构造函数的执行顺序。initializer list-->父类构造函数-->子类构造函数
-
Const
构造函数。要求使用时需要类中有final量。
class Point{ final num x; final num y; const Point(this.x, this.y); const Point.test(this.x, this.y); } main(List<String> arguments) { var point = Point(15, 16); var point2 = Point(15, 16); print(point == point2);//false var point3 = const Point(1, 1); var point4 = const Point(1, 1); print(point3 == point4);//true var point5 = const Point(1, 2); var point6 = const Point(1, 3); print(point5 == point6);//false var point7 = const Point.test(1, 2); var point8 = const Point(1, 2); print(point7 == point8);//true }
-
factory
构造函数
class Message { String content; static final Map<String, Message> _map = {};//静态缓存 factory Message(String content) { return _map.putIfAbsent(content, ()=>Message._internal(content));//找不到则执行该方法进行添加 } Message._internal(this.content);//内部调用的构造函数 } main(List<String> arguments) { var message1 = Message("asd"); var message2 = Message("asd"); print(message1 == message2);//true var message3 = Message("asd3"); print(message1 == message3);//false }
- 纯类名构造函数只允许有一个。默认为
- 属性
class Message {
String _content = "";
get content => _content;
set content(String value) => _content = value;
get content2 { return _content; }
set content2(String value) { _content = value; }
}
main(List<String> arguments) {
var message = Message();
print(message.content);
print(message.content2);
message.content = "hello world";
print(message.content);
message.content2 = "hello world2";
print(message.content2);
}
- 抽象类:关键字
abstract
与C#一样,只有抽象类中才能有抽象属性,方法。
abstract class Message {
String _content = "";
get content;
func();
func2(){//有实现的不算抽象方法
return 16;
}
}
class Message2 extends Message{
@override
func() {
// TODO: implement func
return null;
}
@override
// TODO: implement content
get content => null;
}
- 类的继承
使用extends
关键字。子类中重新实现了父类的方法的话默认重载。如果真的需要重载的话,记得最好加上@overrider
。另外,需要的话记得加上super.xxx()
来调用父类的方法。下面是例子:
class Add{
num add(num a, num b){
return a + b;
}
test(){}
}
class SubAdd extends Add{
@override
num add(num a, num b){
super.test();
super.add(a, b);
return a * b;
}
}
main(List<String> arguments) {
var test = SubAdd();
print(test.add(10, 20));
}
- Implicit接口
Dart中没有像C#中的Interface的关键字,但Dart的每一个类都相当于定义了一个隐式的接口。如果ClassA想实现ClassB的功能,那么ClassA需要implements
ClassB。下面是例子:
class Add{
num add(num a, num b){
return a + b;
}
num other(num a, num b){
return (a + b) / (a - b);
}
}
class Sub{
num sub(num a, num b){
return a - b;
}
}
class Calculation implements Add, Sub{//implements后面的类,仅仅作为接口,接口需要自己额外进行实现,和原来的类里有没有实现没关系
@override
num add(num a, num b) {
return a + b;
}
@override
num other(num a, num b) {
return a * b;
}
@override
num sub(num a, num b) {
return a - b;
}
}
main(List<String> arguments) {
var calculation = Calculation();
print(calculation.add(10, 20));
print(calculation.other(10, 20));
print(calculation.sub(10, 20));
}
- Static:用法和C#一样,但有些需要注意的点。
- 类中的静态变量,如果从没有被使用过,它是不初始化的。
- 类中的静态方法,如果可以的话,推荐将静态方法改成顶级方法。
枚举类型
与C#基本一样
enum Color {
Red,//不能像C#一样额外定义枚举的Index值
Green,
Blue,
Yellow,
}
main(List<String> arguments) {
var colors = Color.values;//转成list,可以用index来取
for(var a in colors){
print(a);
}
}
Mixins
我额外参考的一篇文章,说明的很详细具体,推荐阅读:【译】Dart | 什么是Mixin
几个重要的关键字:mixin
on
with
,相关的内容在下面的例子里注释说明。
觉得这个功能在有些时候会很有用,但现在自己还没有实际操作过,理解不够,只能先记下来,留着以后再补充了。
class DoA{
factory DoA._(){//这里通过工厂方式,禁止外部创建。当然也可以根据需求允许创建
return null;
}
void canDoSomethingA(){
print("do A");
}
}
mixin DoB{//mixin关键字,使该类没有构造器,无法被创建。
void canDoSomethingB(){
print("do B");
}
}
mixin DoC{
void canDoSomethingC(){
print("do C");
}
}
mixin DoD on Person {//mixin...on... ,相当于 class ... extends ...
void canDoSomethingD(){
print("do D");
}
}
class Person {
void Say(String content){
print("Say : $content");
}
}
class Person1 extends Person with DoA, DoB {}
class Person2 extends Person with DoB, DoC {}
class Person3 extends Person with DoC, DoD {}
main(List<String> arguments) {
var person1 = Person1();
print(person1);
person1.canDoSomethingA();
person1.canDoSomethingB();
print(person1 is Person);//true
print(person1 is DoA);//true
print(person1 is DoB);//true
var person2 = Person2();
print(person2);
person2.canDoSomethingB();
person2.canDoSomethingC();
var person3 = Person3();
print(person3);
person3.canDoSomethingC();
person3.canDoSomethingD();
person3.Say("Hello World");
}
泛型
和C#中的泛型用法相同,但是约束语法不一样,也不能像C#一样有多个约束条件(因为Dart中没有接口)。
下面是例子:
class Person {}
class Test<T extends Person>{
void Record(){
print(T);
}
K DoSomething<K extends num>(K value){
print("$K $value");
return value * 2;
}
}
main(List<String> arguments) {
var test = Test<Person>();
test.Record();
print(test.DoSomething<int>(10));
print(test.DoSomething<double>(10.11));
}
Libraries
通俗的讲,就是通过导入的方式,来使用其他脚本里的方法,类,变量等等。
-
import 'xxxx' [as xxx] [show aaa[, aaa2, aaa3]] [hide bbb[, bbb1, bbb2, bbb3]];
方括号中表示可选。
其中:as
表示定义lib使用时前缀,show
表示将lib中指定的方法、类进行导出,hide
表示将指定的方法、类进行隐藏。 -
deferred as
,表示使用时再导入(Flutter 不支持)。
import 'package:dart_demo/dart_demo.dart' deferred as dart_demo;
Future load(Function action) async{
await dart_demo.loadLibrary();
action();
}
main(List<String> arguments) {
load(()=>print(dart_demo.calculate1()));
}
异步
- 一般异步:
关键字async
await
loadRes(String path) async{ print(path); } initRes() async{ await loadRes("path1"); await loadRes("path2"); await loadRes("path3"); } main(List<String> arguments) { var future = initRes(); future.whenComplete(()=>print("finished")); }
- 流
其实和上面的一样,就是await在流里面有个特殊用法。read() async { try { var path = r"C:\Users\Admin\Desktop\test.txt"; var file = File(path); var stream = file.openRead(); await for(var a in stream) { //等待流读取 print(a); } } catch(e) { print(e); } } main(List<String> arguments) { read(); }
其他的零碎东西
需要额外记的关键字
- final 相当于 C#中的 readonly。可以直接用final进行变量的声明,而不用额外去指定变量类型。eg.
final a = 15;
- const,除了其他一般语言的编译时常量的用法以外,还可以用来创建不可改变的值。eg.
var list = const [];
-
is!
:obj is! TypeA
如果obj
不是TypeA
类型则返回true
-
..
:一个语法糖,让你能够对一个对象进行连续的一些列操作,而不用额外去写对象的变量名称。下面是官方例子,两组表达式进行的操作是完全相同的。支持嵌套(这个个人不推荐,嵌套太多容易出坑)。querySelector('#confirm') // Get an object. ..text = 'Confirm' // Use its members. ..classes.add('important') ..onClick.listen((e) => window.alert('Confirmed!')); var button = querySelector('#confirm'); button.text = 'Confirm'; button.classes.add('important'); button.onClick.listen((e) => window.alert('Confirmed!'));
-
?=
:b ?= 5;
,如果b
是null
,则将b
赋值为5
,如果b
不是null
,则b
保持原样。 -
~/
:整除 -
~
:按位取反
Generators
关键字:sync*
async*
yield
yield*
- 同步的返回Iterable<T>,类似于C#中的IEnumerable<T>
Iterable<int> test() sync* {//sync* 标记当前函数为Generators函数,返回Iterable<T> 类型
for(int i=0;i<100;i++) {
yield i;//表示将什么值放进Iterable<T>里面
}
}
- 异步的返回Stream<T>
Stream<int> test(int startNum) async* { //async*标记函数为异步的Generators,返回Stream<T>
if(startNum < 10) {
yield startNum;
yield* test(startNum + 1); //递归要用yield*
}
}
main(List<String> arguments) async {
var st = test(1);
await for(var a in st) {
print(a);
}
}
Callable classes
可以像使用函数一样使用这种类的对象。
class Test {
String content;
Test(this.content);
call(String other) => "$other $content";//实现call方法后,示例的对象可以作为函数对象来使用。可以有传参,也可以没有
}
main(List<String> arguments) async {
var t = Test("hello world");
print(t("Tina"));//使用执行函数的方式,调用类中的call()方法
}
Isolates
目前的简单理解是多线程,不一定正确&需要进行实践。等后面遇到了,理解了再将笔记补上。
Typedefs
目前的理解,是给函数的类型定一个别名。感觉形式上有点像C#里面的委托。
typedef num Calculate(num a, num b);//将这种类型的函数定义为Calculate类型的函数
num add(num x, num y) => x + y;
sub(num x, num y) => x - y;
main(List<String> arguments) async {
print(add is Calculate);//true
print(sub is Calculate);//false
}
Metadata
- 使用
Metadata能够在类,变量,参数,函数等等的前面使用。运行时,可以通过反射获取到Metadata。
class Television {
/// _Deprecated: Use [turnOn] instead._
@deprecated//表示这个函数方法被弃用
void activate() {
turnOn();
}
/// Turns the TV's power on.
void turnOn() {...}
}
- 创建
官方例子
library todo;
class Todo {
final String who;
final String what;
const Todo(this.who, this.what);
}
import 'todo.dart';
@Todo('seth', 'make this do something')
void doSomething() {
print('do something');
}
注释
需要说的就是文档注释,其他的和C#一样。
方括号中表示变量
/// Exercises your llama with an [activity] for
/// [timeLimit] minutes.
void exercise(Activity activity, int timeLimit) {
// ...
}
官方文档,基础的部分算是看完了。后面实际操作中发现有问题会进行补充和修改。
如果有人看的话,发现错误欢迎指正。
另外发现ide的自动格式化有些不好用,有好用的ide也欢迎推荐。