Dart 语法 学习笔记

语法学习笔记。资料参考:官方文档(英文)
(要直接看英文文档,中文文档可能是机器翻译的,很多地方语句不通顺,埋坑无数)


语言的特性

  1. 所有能放进变量中的都是对象,所有对象都是类的实例,所有对象都继承于Object。包含数字,方法,null等等。也由此可以知道:没有初始化的变量都是null;没有装箱拆箱的问题。
  2. Dart是强类型语言。你可以显示声明类型,也可以直接使用var,Dart会自动推断类型。当你要声明一个不确定类型的变量时,可以使用特殊类型dynamic来声明。
  3. 支持顶级方法,类方法,嵌套方法(在方法中声明方法)
  4. 没有其他语言的public等关键字,但如果一个标识符以 "_" 开头,则为私有。
  5. new关键字是可选的。

内置类型

  1. numbers:带小数点的就是double,不带的就是int

  2. Strings:
    1.创建既可以用""也可以用''
    2.编码是UTF-16;
    3.可以使用"${xxx}"来格式化输出字符串,其中的{}一般情况下可以省略,eg."a = $a" "a = ${a}." "a = ${a}ah",其中最后一个需要加{}是因为如果不加{},相当于"a = $aah",使用的值是aah,而不是a

    1. 多行字符串''' xxxx ''' 或者 """ xxx """
    2. r'\n' 输出的是 \n。其中r取消字符串中的转义字符。
  3. Booleans:不允许使用if(0)之类的代码,会编译报错。

  4. Lists:

    1. 索引从0开始
    2. 使用const创建的list,该变list中的单个元素会运行报错。
      var list = const[1, 2, 3, 4]; list[0] = 12;//这里报错
    3. 可以在list创建时使用iffor
        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);
      
  5. Sets:无序列表

    1. 创建
        //var test = {}; //这样声明的是map,不是set
        var test = {};
        var test1 = {"a", "b", "c", "d", "e"};
        var test2 = {};
        test2.add("f");
        test2.addAll(test1);
      
    2. 一样可以用const进行创建,使用const创建的set不能更改。
  6. Maps:字典

    1. 创建
      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;
      
    2. 可以使用const进行创建,使用const创建的Map不能更改。
  7. Runes:使用4位16进制数可以输出一些特殊字符。但一般好像没什么卵用。

  Runes input = new Runes(
      '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
  print(new String.fromCharCodes(input));

输出:

  1. Symbols:不清楚这个东西是干嘛的,好像也没什么卵用。

函数

  1. 函数的返回值类型可以省略,下面两个函数是一样的。
    add(num a, num b) {
      return a + b;
    }
    num add(num a, num b) {
      return a + b;
    }
  1. 不定参数
```
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 arguments) {
  print(add(1, 2, 3));//和其他语言使用方法一致
  print(sub(b : 2, a : 1));//传入参数是按照key值对应的,不用考虑顺序。&&个人认为这种方式使用有些麻烦。
}
```
  1. 函数能够直接当做变量进行传递,类似于C#中的Action,但不用额外进行变量的定义。
  2. 匿名函数,使用lamda表达式,语法与C#基本一致。
  3. 值的一看的官方闭包的例子,用函数创建函数。
/// 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);
}

关于++ii++

和其他语言的一样。
这里只是简单记录一下,顺便复习,&&知道这么个东西就行了,开发中不要用,不差多一行代码。
话说谁要是在写代码的时候抖这个机灵,不出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靠近= ==> 先赋值

流程控制

  1. if(xxx){} else if(xxx) {} else{}
  2. for(int i = 0; i < xxx.Length; i++) && for(var a in xxx)for ... in 的用法基本等同于C#中的foreach
  3. while(xxx){} && do{}while{xxx}
  4. 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;
}
  1. 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等关键字就好了。下面记一下有区别的地方。

  1. 构造函数
    1. 纯类名构造函数只允许有一个。默认为ClassName()。父类额外定义的构造函数,子类至少要继承声明一个。
    2. 特有的构造函数声明方法
    class Point {
    num x, y;
    
      Point(this.x, this.y);//省略赋值。这种方式的构造函数只能有一个。
      Point.origin() {//命名构造函数,使用时调用Point.origin()即可。多个构造函数可以用此定义。
         x = 0;
         y = 0;
     }
    }
    
    1. 构造函数不能自动继承,需要手动实现。
    class Animal {
      num age;
      Animal(this.age);//定义初始构造函数,唯一
      Animal.normal()  :  this(0);//使用默认构造函数
    
      Animal.dead() {
        age = 9999;
      }
    }
    
    class Dog extends Animal {
       Dog(num age) : super(age);//至少实现一个
    }
    
    1. 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");
        }
      }
    
    1. 创建对象时,构造函数的执行顺序。initializer list-->父类构造函数-->子类构造函数
    2. 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 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
    }
    
    1. factory构造函数
      class Message {
        String content;
    
        static final Map _map = {};//静态缓存
    
        factory Message(String content) {
          return _map.putIfAbsent(content, ()=>Message._internal(content));//找不到则执行该方法进行添加
        }
    
        Message._internal(this.content);//内部调用的构造函数
      }
    
      main(List arguments) {
        var message1 = Message("asd");
        var message2 = Message("asd");
        print(message1 == message2);//true
    
        var message3 = Message("asd3");
        print(message1 == message3);//false
      }
    
  2. 属性
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 arguments) {
  var message = Message();
  print(message.content);
  print(message.content2);
  message.content = "hello world";
  print(message.content);
  message.content2 = "hello world2";
  print(message.content2);
}
  1. 抽象类:关键字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;
}
  1. 类的继承
    使用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 arguments) {
  var test = SubAdd();
  print(test.add(10, 20));
}
  1. Implicit接口
    Dart中没有像C#中的Interface的关键字,但Dart的每一个类都相当于定义了一个隐式的接口。如果ClassA想实现ClassB的功能,那么ClassA需要implementsClassB。下面是例子:
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 arguments) {
  var calculation = Calculation();

  print(calculation.add(10, 20));
  print(calculation.other(10, 20));
  print(calculation.sub(10, 20));
}
  1. Static:用法和C#一样,但有些需要注意的点。
    1. 类中的静态变量,如果从没有被使用过,它是不初始化的。
    2. 类中的静态方法,如果可以的话,推荐将静态方法改成顶级方法。

枚举类型

与C#基本一样

enum Color {
  Red,//不能像C#一样额外定义枚举的Index值
  Green,
  Blue,
  Yellow,
}

main(List 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 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{
  void Record(){
    print(T);
  }

  K DoSomething(K value){
    print("$K  $value");
    return value * 2;
  }
}

main(List arguments) {
  var test = Test();
  test.Record();
  print(test.DoSomething(10));
  print(test.DoSomething(10.11));
}

Libraries

通俗的讲,就是通过导入的方式,来使用其他脚本里的方法,类,变量等等。

  1. import 'xxxx' [as xxx] [show aaa[, aaa2, aaa3]] [hide bbb[, bbb1, bbb2, bbb3]];方括号中表示可选。
    其中:as表示定义lib使用时前缀,show表示将lib中指定的方法、类进行导出, hide表示将指定的方法、类进行隐藏。
  2. 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 arguments) {
  load(()=>print(dart_demo.calculate1()));
}

异步

  1. 一般异步:
    关键字 async await
    loadRes(String path) async{
      print(path);
    }
    
    initRes() async{
      await loadRes("path1");
      await loadRes("path2");
      await loadRes("path3");
    }
    
    main(List arguments) {
      var future = initRes();
      future.whenComplete(()=>print("finished"));
    }
    

  2. 其实和上面的一样,就是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 arguments) {
      read();
    }
    

其他的零碎东西

需要额外记的关键字
  1. final 相当于 C#中的 readonly。可以直接用final进行变量的声明,而不用额外去指定变量类型。eg. final a = 15;
  2. const,除了其他一般语言的编译时常量的用法以外,还可以用来创建不可改变的值。eg. var list = const [];
  3. is!obj is! TypeA 如果obj不是TypeA类型则返回true
  4. ..:一个语法糖,让你能够对一个对象进行连续的一些列操作,而不用额外去写对象的变量名称。下面是官方例子,两组表达式进行的操作是完全相同的。支持嵌套(这个个人不推荐,嵌套太多容易出坑)。
    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!'));
    
  5. ?=b ?= 5;,如果bnull,则将b赋值为5,如果b不是null,则b保持原样。
  6. ~/:整除
  7. ~:按位取反
Generators

关键字:sync* async* yield yield*

  1. 同步的返回Iterable,类似于C#中的IEnumerable
Iterable test() sync* {//sync* 标记当前函数为Generators函数,返回Iterable 类型
  for(int i=0;i<100;i++) {
    yield i;//表示将什么值放进Iterable里面
  }
}
  1. 异步的返回Stream
Stream test(int startNum) async* { //async*标记函数为异步的Generators,返回Stream
  if(startNum < 10) {
    yield startNum;
    yield* test(startNum + 1); //递归要用yield*
  }
}

main(List 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 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 arguments) async {
  print(add is Calculate);//true
  print(sub is Calculate);//false
}
Metadata
  1. 使用
    Metadata能够在类,变量,参数,函数等等的前面使用。运行时,可以通过反射获取到Metadata。
class Television {
  /// _Deprecated: Use [turnOn] instead._
  @deprecated//表示这个函数方法被弃用
  void activate() {
    turnOn();
  }

  /// Turns the TV's power on.
  void turnOn() {...}
}
  1. 创建
    官方例子
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也欢迎推荐。

你可能感兴趣的:(Dart 语法 学习笔记)