该部分内容主要分为四部分, 认真看, 一小时, 带你学完Dart基础语法:
测试工具我选择的是VSCode, 打开空白文件夹开始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)
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将报错
final声明常量
final height = 1.88;
height = 2.00;
代码报错: The final variable 'height' can only be set once.
1.88即为最后一次赋值, 不能够再次赋值
const声明常量
const address = "北京市";
address = "上海市";
代码报错: Constant variables can't be assigned a value.
常量不能被赋值
-
final和const的区别- const 必须赋值, 常量值(编译期间需要有一个确定的值)
- final 可以通过计算/函数获取一个值(运行期间来获取一个值)
- 例: 获取当前时间, 是运行时行为, date1的声明就会报错, date2的写法才是正确的:
const date1 = DateTime.now(); ❌代码报错: Const variables must be initialized with a constant value.
const修饰的变量必须初始化一个确定的常量值, 所以是错误的写法
final date2 = DateTime.now(); ✅- 没有使用
final或const修饰的变量的值是可以被更改的,即使这些变量之前引用过 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); // 477740407class 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
int 和 double 都是 num 的子类。 double 即可以是整型, 也可以是浮点型, 而int 只能是整型
3.3 字符串类型
- 定义字符串
可分为
单引号、双引号、三引号定义
var str1 = 'abc'; // 单引号
var str2 = "abc"; // 双引号
var str3 = """
你好,
我好;
大家好!
"""; // 三引号, 带有排版格式的字符串
- 使用
单引号创建字符串时可以使用斜杠(\)来转义那些与单引号冲突的字符串
var str4 = '使用单引号创建字符串时可以使用\'斜杠\'来转义那些与单引号冲突的字符串';
print(str4); // 使用单引号创建字符串时可以使用'斜杠'来转义那些与单引号冲突的字符串
- 字符串和表达式进行拼接
- 通过美元符号
${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 ([])
- 通过
[]直接包装定义
var list = ["a", "b", "c"];
print(list); // [a, b, c]
- 拓展操作符
...和...?
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);
- 指定类型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 ({})
- Map 是用来关联的对 keys 和 values 的对象。其中键和值都可以是任何类型象。但是键必须是可哈希的, 一般便于理解都是String
var info = {
"name": "张三",
"age": 18,
20: 10
};
print(info); // {name: 张三, age: 18, 20: 10}
-
初始化一个
{}, 默认初始化的到底是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 可选参数 (位置可选参数 / 命名可选参数)
- 位置可选参数, 使用中括号包装起来:
[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);
- 命名可选参数, 使用花括号包装起来:
{int age, double height}, 顾名思义, 就是实参和形参在进行匹配时, 是根据参数名称进行匹配的
/* 命名可选参数
- 第一个参数name为必传,
- 剩下需要传递什么参数直接指定即可
*/
void sayHello3(String name, {int? age, double? height}) {}
// 指定传入height 参数
sayHello3("张三", height: 1.88);
- 可选参数的默认值 在可选参数的后面直接指定即可:
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 函数可以作为另一个函数的参数
- 定义一个无参数无返回值的函数, 用作另一个函数的参数
void forTest() {
print("函数作为参数被调用");
}
- 定义一个无返回值, 函数参数 为一个函数 的函数
void Test(Function foo) {
foo();
}
- 使用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该种方式定义函数, 因为使用接口的人不清楚是否需要传递参数给你, 或者是否要接收返回值.
- 当你继续使用
Function来定函数参数:
void test5(Function foo) {
foo();
}
void test6(Function foo) {
foo("123");
}
在调用test5、test6时, 依然可以通过老办法调用:
test5(() {
print("test5"); // test5
});
test6((name) {
print(name); // 张三
});
但是会发现, 不能清晰的了解到, 用来作为参数的匿名函数 是否需要参数或者返回值, 因此具有一定的迷惑性.
- 指定 作为函数参数的 函数 的参数和返回值, 但是阅读性还是不高, 比较乱:
// 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
- 使用
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);
- 函数最为函数的返回值
Calculate demo() {
return (num1, num2) {
return num1 * num2;
};
}
// 声明一个函数, 来接收demo函数的返回值
var mutiplyDemo = demo();
// mutiplyDemo 是一个返回值为int类型, 有两个参数分别也为int 类型的函数
var result = mutiplyDemo(10, 20);
print(result); // 200
结语
路漫漫其修远兮,吾将上下而求索~
.End