Dart语法精炼(一)

170 阅读5分钟

该部分内容主要分为四部分, 认真看, 一小时, 带你学完Dart基础语法:

测试工具我选择的是VSCode, 打开空白文件夹开始Dart基础语法的学习吧...

dart_learn的仓库

Dart语法精炼(一)

Dart语法精炼(二)

Dart语法精炼(三)

Dart语法精炼(四)

Dart中文网

01. 函数入口

void main(List<String> args) {
  print('hello world');
}

1.1 main函数是Dart 的入口

1.2 Dart中使用print函数进行打印操作

1.3 每一句代码结束都要加分号 (;)

1.4 关于dart文件的运行

- 4.1 安装`code runner` 插件
已安装code runner 插件之后, 右侧就多出了一个 '三角' 运行的按钮

或者使用`control option n` 快捷键来运行代码(run code)
- 4.2 未安装`code runner` 插件, 只能通过终端命令来运行文件
打开终端窗口:

`cd /dart` 文件目录下

`dart 01.函数入口.dart` 即可运行

1.5 常用快捷键

- command + /                单行注释
- option + shift + a         多行注释
- option + shift + f         格式化代码
- control ~                  终端显隐
- command shift k            删除当前行
- command b                  左侧边栏显隐

1.6 VSCode代码自动保存

- vccode -> 左上角文件 -> 自动保存(勾选即可)

02. 声明变量

2.1 明确的声明

  String name = "张三";
  print("$name");
  print(name);

打印结果:

张三

张三

2.2 类型推导(var/final/const)

  1. var声明变量
  var age = 20;
  age = "abc";

代码报错: A value of type 'String' can't be assigned to a variable of type 'int'.

变量age 虽然没有明确类型, 但是已经被20推导成int类型, 在此赋值String将报错

  1. final声明常量
  final height = 1.88;
  height = 2.00;

代码报错: The final variable 'height' can only be set once.

1.88即为最后一次赋值, 不能够再次赋值

  1. const声明常量
  const address = "北京市";
  address = "上海市";

代码报错: Constant variables can't be assigned a value.

常量不能被赋值

  1. finalconst 的区别

    • const 必须赋值, 常量值(编译期间需要有一个确定的值)
    • final 可以通过计算/函数获取一个值(运行期间来获取一个值)
    • 例: 获取当前时间, 是运行时行为, date1的声明就会报错, date2的写法才是正确的:
      const date1 = DateTime.now();   ❌
    

    代码报错: Const variables must be initialized with a constant value.

    const修饰的变量必须初始化一个确定的常量值, 所以是错误的写法

       final date2 = DateTime.now();  ✅
    
    • 没有使用 finalconst 修饰的变量的值是可以被更改的,即使这些变量之前引用过 const 的值。比如:
       var list = const [];
       list[0] = 10;     // ❌这句话运行起来就会崩溃, 因为list是一个不可变的数组, 修改其中元素造成崩溃
       list = [1, 2, 3]; // ✅可以对未被const/final关键字修饰的变量进行整体的修改
       print(list);      // [1, 2, 3]
    
    • final一般用的比较多, 因为一般多数情况都是在运行时获取到某一值
    • const多用于获取获取同一对象, 节省堆内存开销
    class Person {
       String? name;
       Person(String name) {
           this.name = name;
       }
    }
    
    • identical 判断两个对象是否相同, 地址是否相同
     final p1 = Person('张三');
     final p2 = Person('张三');
     print(identical(p1, p2)); // false, 相当于在堆中开辟了两块空间, 初始化了相同的字符串`张三`
     print(p1.hashCode);       // 609349088
     print(p2.hashCode);       // 477740407
    
     class Person2 {
         final String name;
         const Person2(this.name);
     }
    
         /* 当使用const 修饰变量时,
           - ❌会报错:Const variables must be initialized with a constant value.
           - const修饰的变量一定要用一个常量来初始化, 所以Person2 的name需要使用final来修饰.
           - 因此需要修改函数的构造方法: Person2.
         */
         const p3 = Person2("jack");
         const p4 = Person2("jack");
         const p5 = Person2("rose");
         print(identical(p3, p4)); // true, 只要通过jack字符串初始化的, 都会得到相同的一个对象, 类似内部帮你完成了指定单例设计
         print(identical(p4, p5)); // flase, 同样开辟了两段内存
         print(p3.hashCode);       // 515121275
         print(p4.hashCode);       // 515121275
         print(p5.hashCode);       // 928928663
    

03. 数据类型

3.1 在dart中, 没有非零即真的说法

  var flag = "";
  if (flag) { // ❌报错: Conditions must have a static type of 'bool'.
    print("true");
  } else {
    print("false");
  }

所以在判断条件时, 条件语句必须是一个bool类型.

  var flag = true;
  if (flag) {
    print("执行代码");
  }

或者:

  bool flag = true;
  if (flag) {
    print("执行代码");
  }

3.2 基本类型int, double

intdouble 都是 num 的子类。 double 即可以是整型, 也可以是浮点型, 而int 只能是整型

3.3 字符串类型

  1. 定义字符串 可分为单引号双引号三引号定义
  var str1 = 'abc'; // 单引号
  var str2 = "abc"; // 双引号
  var str3 = """
  你好,
  我好;
  大家好!
  """; // 三引号, 带有排版格式的字符串
  1. 使用单引号创建字符串时可以使用斜杠(\)来转义那些与单引号冲突的字符串
  var str4 = '使用单引号创建字符串时可以使用\'斜杠\'来转义那些与单引号冲突的字符串';
  print(str4); // 使用单引号创建字符串时可以使用'斜杠'来转义那些与单引号冲突的字符串
  1. 字符串和表达式进行拼接
  • 通过美元符号${variable}, 对变量进行拼接
  • Object 对象的 runtimeType 属性在运行时获取一个对象的类型,该对象类型是 Type 的实例
  var name = "tom";
  var age = 19;
  var height = 1.88;

  var message1 = "my name is ${name}, age is ${age}, height is ${height}";
  var message2 =
      "name is ${name}, type is ${name.runtimeType}"; // runtimeType获取name类型
  print(message1); // my name is tom, age is 19, height is 1.88
  print(message2); // name is tom, type is String
  • ${}里面为单纯的变量时, 花括弧可以省略
  var message3 = "my name is $name, age is $age, height is $height";
  print(message3); // my name is tom, age is 19, height is 1.88
  • ${}里面为表达式时, 花括弧不可以省略
  var message4 = "name is $name, type is $name.runtimeType";
  print(message4); // name is tom, type is tom.runtimeType, 所以message4 中的name.runtimeType 的花括弧不能省略
  
  var message5 = "name is $name, height is ${height + 9.66}";
  print(message5); // name is tom, height is 11.54, 花括弧不能省略
  • 通过+拼接
  var message6 = name + "$age" + "$height";
  print(message6); // tom191.88

  var message7 = "hello" + " " + name;
  print(message7); // hello tom

04. 集合类型

4.1 列表List ([])

  1. 通过[] 直接包装定义
  var list = ["a", "b", "c"];
  print(list); // [a, b, c]
  1. 拓展操作符......?
  var list = ["a", "b", "c"];
  var list2 = ["d", ...list]; // 将list 追加至list2之中
  print(list2);
  // 如果扩展操作符右边可能为 null ,你可以使用 null-aware 扩展操作符(...?)来避免产生异常
  var listNull = null;
  var listAddNull = ["e", ...?listNull];
  print(listAddNull);
  1. 指定类型List, <类型>[]
  // 指定类型, 类型必须相同
  var list3 = <String>[];

  // 不指定类型, 类型可以不同
  var list4 = [1, 'a', 1.88];

4.2 集合Set ({})

  • 在 Dart 中,Set 是一组特定元素的无序集合不可重复。和Map及其相似, set内部的类型必须相同
  var movies = {"你好", "我好", "大家好"};
  print(movies); // {你好, 我好, 大家好}
  • 初始化一个Set, var names = <指定类型>{} 或者 Set<指定类型> name = {}
  var movies1 = <int>{};
  Set<int> movies2 = {};
  • Set最常用的地方就是去重
  var list5 = [3, 1, 2, 3, 4, 4, 4];
  var set = Set.from(list5); // 结果是一个set
  var list6 = set.toList();  // 将set 转化为list
  print(list6);              // [3, 1, 2, 4]

4.3 映射Map ({})

  1. Map 是用来关联的对 keys 和 values 的对象。其中键和值都可以是任何类型象。但是键必须是可哈希的, 一般便于理解都是String
  var info = {
    "name": "张三",
    "age": 18, 
    20: 10
  };
  print(info); // {name: 张三, age: 18, 20: 10}
  1. 初始化一个{}, 默认初始化的到底是Set? 还是Map?

    • {}默认为Map, 当你需要初始化一个默认Set时, 要指定类型.
      var par = {}; // Creates a map, not a set.
      print(par.runtimeType); // _Map<dynamic, dynamic>
    
      var par2 = <int>{}; // Creates a set
      print(par2.runtimeType); // _Set<int>
    

05. 函数的基本使用

函数格式为:

返回值 函数名称 (参数类型 参数1, 参数类型 参数2, ...) { // 函数体}

5.1 函数的返回值可以省略

  • 根据返回值默认推导出返回值类型, 但是不建议省略, 省略的话会降低了代码的可读性.
int sum(int num1, int num2) {
  return num1 + num2;
}

// 根据返回值 num1 + num2的结果, 自动推导出函数返回值类型为double
sum2(double num1, double num2) {
  return num1 + num2;
}
  var result = sum(1, 2);
  var result2 = sum2(3, 4);
  print(result);   // 3
  print(result2);  // 7.0

5.2 dart 中没有函数重载(函数名相同, 参数不同)

int sum(int num1, int num2) {
  return num1 + num2;
}

// ❌报错: The name 'sum' is already defined.
int sum(int num1, int num2, double num3) {
  return num1 + num2;
}

一旦原有接口需要添加参数, 因为不支持重载, 所以需要通过命名构造函数.withXXXX(this.对应添加的参数来解决), 或者.withMap(MAP<String: String>)来达到函数重载的目的.

5.3 函数的必选参数 (必须传)

void sayHello1(String name) {
  print("hello" + " " + "$name");
}
  // 调用函数时, 必须传递一个String
  sayHello1("张三"); // hello 张三

5.4 可选参数 (位置可选参数 / 命名可选参数)

  1. 位置可选参数, 使用中括号包装起来: [int age, double height], 顾名思义, 就是实参和形参在进行匹配时, 是根据位置进行匹配的
/* 位置可选参数
   - 第一个参数name为必传,
   - 第二参数为选传, 必须为int类型, 且必须代表age,
   - 第三各个参数类型为double, 代表height, 都是会进行严格校验的 
 */
void sayHello2(String name, [int? age, double? height]) {}
  // ❌age参数类型匹配出错: The argument type 'double' can't be assigned to the parameter type 
  sayHello2("张三", 1.0, 1.88);
  // ✅正确使用
  sayHello2("张三", 10, 1.88);
  1. 命名可选参数, 使用花括号包装起来: {int age, double height}, 顾名思义, 就是实参和形参在进行匹配时, 是根据参数名称进行匹配的
/* 命名可选参数
    - 第一个参数name为必传, 
    - 剩下需要传递什么参数直接指定即可
 */
void sayHello3(String name, {int? age, double? height}) {}
  // 指定传入height 参数
  sayHello3("张三", height: 1.88); 
  1. 可选参数的默认值 在可选参数的后面直接指定即可:
void sayHello4(String name, [int age = 10, double height = 1.88]) {
  print('位置可选参数设置了默认值:' + name + '$age' + '$height');
}

void sayHello5(String name, {int age = 10, double height = 1.88}) {
  print('命名可选参数设置了默认值:' + name + '$age' + '$height');
}
  sayHello4('张三'); // 位置可选参数设置了默认值:张三101.88
  sayHello5('李四'); // 命名可选参数设置了默认值:李四101.88

5.5 函数可以作为另一个函数的参数

  1. 定义一个无参数无返回值的函数, 用作另一个函数的参数
void forTest() {
  print("函数作为参数被调用");
}
  1. 定义一个无返回值, 函数参数 为一个函数 的函数
void Test(Function foo) {
  foo();
}
  1. 使用Test函数时, 传递forTest为函数参数
  Test(forTest); // 函数作为参数被调用

5.6 匿名函数

(参数列表) {函数体}

匿名函数, 一般只用来作为函数参数, 作用就是不需要单独声明函数

  Test(() {
    print("无返回值的匿名函数 作为函数参数, 被调用啦");
  });

当作为参数的函数, 存在返回值的情况下, 在测试函数中内部接收:

void Test2(Function foo) {
  var result = foo();
  print(result); // 10
}

void test3(Function foo) {
  var result = foo();
  print("result = $result"); // result = nihao
}
  Test2(() {
    print("有返回值的匿名函数 作为函数参数, 被调用啦");
    return 10;
  });
  
  test3(() {
    return "nihao";
  });

5.7 箭头函数

当函数体只有一句代码时, 即可使用箭头函数, 格式为: 函数体替换为=> 一句代码

void test4(Function foo) {
  foo();
}

使用时, 二者等价:

  test4(() => print("箭头函数被执行"));
  // 等价于
  test4(() {
    print("箭头函数被执行");
  });

5.8 typedef 关键字

  • typedef 封装正式开发, 增强阅读性
  • 正式开发中, 不可使用Function 该种方式定义函数, 因为使用接口的人不清楚是否需要传递参数给你, 或者是否要接收返回值.
  1. 当你继续使用Function 来定函数参数:
void test5(Function foo) {
  foo();
}

void test6(Function foo) {
  foo("123");
}

在调用test5、test6时, 依然可以通过老办法调用:

  test5(() {
    print("test5"); // test5
  });

  test6((name) {
    print(name);    // 张三
  });

但是会发现, 不能清晰的了解到, 用来作为参数的匿名函数 是否需要参数或者返回值, 因此具有一定的迷惑性.

  1. 指定 作为函数参数的 函数 的参数和返回值, 但是阅读性还是不高, 比较乱:
// test7 的参数是一个返回值 和两个参数都为 int类型的函数
void test7(int foo(int num1, int num2)) {
  var result = foo(20, 30);
  print("test7 result = $result");
}

// 所以定义一个返回值和两个参数都为int 类型的函数
int sum(int a, int b) {
  return a + b;
}

在使用上:

  test7(sum); // test7 result = 50
  // 等价与
  test7((num1, num2) => num1 * num2); // test7 result = 600
  1. 使用typedef 定义函数参数
// 定义一个返回值为int类型, 有两个参数分别也为int 类型的函数, 叫做Calculate
// 从此以后Calculate, 就代表这个函数
typedef Calculate = int Function(int num1, int num2);

void test8(Calculate calc) {
  var result = calc(30, 30);
  print("test8 result = $result"); // test8 result = 900
}
  // 函数调用
  test8((num1, num2) => num1 * num2);
  1. 函数最为函数的返回值
Calculate demo() {
  return (num1, num2) {
    return num1 * num2;
  };
}
  // 声明一个函数, 来接收demo函数的返回值
  var mutiplyDemo = demo();
  // mutiplyDemo 是一个返回值为int类型, 有两个参数分别也为int 类型的函数
  var result = mutiplyDemo(10, 20);
  print(result); // 200

结语

路漫漫其修远兮,吾将上下而求索~

作者简书

作者GitHub

.End