Dart 语法学习目录
第一节: Dart 语法了解,认识变量,常量,数据类型
第二节: Dart 操作符(运算符)
第三节: Dart 中常用集合 List/Map
第四节: Dart 函数使用,声明函数/匿名函数/可选参数/作用域/闭包
第五节: Dart 面向对象/构造函数/静态属性
第六节: Dart中抽象类abstract/接口implements/混入Mixin
第七节: Dart中泛型/泛型定义与使用
第八节: Dart 中的库/自定义库/内置库/第三方库
函数的了解:
- Dart 是一门真正面向对象的语言, 甚至其中的函数也是对象,并且有它的类型 Function 。
- 这也意味着函数可以被赋值给变量或者作为参数传递给其他函数。 也可以把 Dart 类的实例当做方法来调用。
- 方法都有返回值,当没有指定返回值的时候,函数返回null,即最后默认执行一句
return null
,可以省略不写。
1. 函数的定义
1.1 普通的声明函数
自定义函数方法的基本格式,
返回类型 函数名称(参数1,参数2,...){
函数体
return 返回值;
}
示例:
int result(int a,int b){
return a + b;
}
接下来好好看看不同的使用情况
1.1.1 定义全局函数,
通过前面的学习,我们都知道dart有一个默认的入口函数main, 如果在函数main之外定义的函数,或是直接在main函数中定义的函数都是全局函数
示例:
// 入口函数main之外定义的全局函数
int fn(){
print("main外的全局函数");
return 123;
}
void main(){
// 入口函数main里的全局函数
int fun(){
print("main里的全局函数");
return 123;
}
// 执行全局函数
fn(); // main外的全局函数
fun(); // main里的全局函数
}
1.1.2 局部函数
除了main入口函数外,在其他函数中定义的函数都是局部函数
示例:
void main(){
// 定义函数
// 1\. fn 全局函数
void fn(){
// 2\. 定义局部作用域函数
// scope是局部函数,只能在fn函数中使用
scope(){
return '你好';
}
// 局部函数只能在其父作用域中调用
print(scope()); // 你好
}
fn();
}
1.1.3 函数的返回值类型可以省略
定义方法的返回值类型
可以省略
void notType(){
print("这个函数没有返回值");
}
notTypeTwo(){
print("这个函数没有返回值");
}
建议明确函数或方法的返回值类型,既便于修改,也方便阅读。
1.1.4 指定函数的返回值类型
函数的返回值类型可以指定为任意类型
示例:
// 1 定义返回整型的函数
int returnInt(){
return 123;
}
// 2 定义返回字符串类型的函数
String returnString(){
return '我是字符串';
}
// 3 定义返回布尔类型的函数
bool returnBoolean(){
return true;
}
// 4 定义返回集合类型的函数
List returnList(){
return ['a','b','c'];
}
// 5 定义返回Map类型的函数
Map returnMap(){
return {"name":"wuwei","age":18};
}
// 入口函数
void main(){
// 1\. 调用返回整型的函数
var num = returnInt();
print(num); // 123
// 2\. 调用返回字符串类型的函数
print(returnString()); // 我是字符串
// 2\. 调用返回布尔类型的函数
print(returnBoolean()); // true
// 4\. 调用返回集合类型的函数
print(returnList()); // [a, b, c]
// 5\. 调用返回Map类型的函数
print(returnMap()); // {name: wuwei, age: 18}
}
1.1.5 定义返回类型为void的情况
如果不指定返回类型,或是指定返回类型却没有返回值,函数默认的返回值为null
示例:
void main(){
// 未定义返回类型
fn () {
print("未定义返回类型");
}
// 定义返回类型无返回值
int fun(){
print("定义返回类型无返回值");
}
// 查看未定义返回类型的定义类型无返回值的返回值
var fn2 = fn(); // 未定义返回类型
print(fn2); // null
var fun2 = fun(); // 定义返回类型无返回值
print(fun2); // null
}
通过示例了解,为定义返回类型,和明确返回类型,如果没有return
关键字,默认返回null
但是有一种特殊情况, 指定函数的返回类型为void
关键字
注意,如果指定函数的返回类型为关键字void
,表示无返回值,为空,这个时候,不能使用函数执行完后赋值的变量,否则就会报错
示例:
void main(){
// 通过关键字 void 指定函数返回值为空
void fn () {
print("返回值为void");
}
// 查看返回值
var fn2 = fn();
print(fn2);
// 程序报错: Error: This expression has type 'void' and can't be used.
}
因此,如果知道函数返回类型,建议定义返回值类型.
如果不知道返回值的类型可以不定义返回值的类型,
如果指定未来不需要用到函数的返回值,函数也不会有返回值,可以使用关键字void
定义返回类型
1.2 函数表达式
定义函数是除了可以使用普通的函数定义方式,还可以定义函数表达式, 将匿名函数赋值给变量
// 定义匿名函数表达式
var fn = (){
print("这是一个匿名函数"); // 这是一个匿名函数
};
void main(){
// 执行匿名函数
fn();
}
函数表达式也可以使用Function
来限定变量
main(){
Function wuwei = (){
print("我是一个函数表达式"); //我是一个函数表达式
};
wuwei();
}
既然讲到匿名函数, 就在来好好看看匿名函数
1.3 匿名函数
1、没有名字的函数,称之为匿名函数
,有时候也被称为lambda
或者 closure 闭包
。
2、你可以把匿名函数赋值给一个变量, 然后你可以通过这个变量使用这个函数,即函数表达式.
匿名函数通常用在函数表达式, 自执行函数,或者一些方法的回调函数
示例:
void main(){
// 1\. 函数表达式
var fn = (){
print("这是一个匿名函数");
};
fn();
// 2\. 自执行函数
// 自执行函数
void main(){
((){
print("这是自执行函数");
})();
}
// 3\. 方法的回调函数
var list = ["苹果","香蕉","橘子"];
list.forEach((value){
print(value);
})'
}
1.4 箭头函数
箭头函数的语法: =>
注意
这个箭头函数跟我们前端所学的
ES6
箭头函数不一样在箭头 (=>) 和分号 (;) 之间只能使用一个 表达式 ,不能是 语句 。
void main(){
// dart箭头函数更像是函数声明,只有一个return语句时的简写
// ES6 的箭头函数是一个函数表达式
int fn(int a, int b) => a + b ;
print(fn(10,20)); // 30
}
错误的写法
void main(){
// 箭头函数不能有大括号, 这样写就会报错
int fn(int a, int b) => {
return a + b
} ;
print(fn(10,20)); // 30
}
1.5 入口函数
每个应用都需要有个顶级的 main()
入口方法才能执行。 main()
方法的返回值为 void
// 入口函数
void main(){
}
2. 函数的参数
2.1 普通函数传参
void main(){
// 1\. 普通函数传参
// 定义函数
String printUserInfo(name, age){
return 'name:$name--age:$age';
}
// 执行函数
print(printUserInfo("小明", 18)) ; // name:小明--age:18
}
普通函数的传参, 参数可以传递任何数据类型.
但是有的时候我们需要考虑用户是否会乱传参数,导致程序崩溃,所以我们可以限定参数类型
2.2 限定传递参数的类型
就像定义变量一样,在形参前使用特定的数据类型来限定形参能接收到 数据类型
void main(){
// 2\. 限定传参类型
// 定义函数
String printUserInfo(String name, int age){
return 'name:$name--age:$age';
}
// 执行函数
print(printUserInfo("小明", 18)) ; // name:小明--age:18
print(printUserInfo("小明", '18')) ; // 报错,第二个参数不符合参数类型
}
限定了传递参数类型后,如果用户传递的参数类型不对就会报错
同时我们回发现, 我们传递的实参个数必须跟形参个数完全对应.有多少个形参就必须传递多少个实参, 这样,用户使用的灵活性就会下降,所以我们可以定义一些可选参数,可选参数在传不传实参都不会报错
2.3 可选参数
使用中括号来确定哪些参数是可选参数
void main(){
// 3\. 可选传参
// 定义函数
String printUserInfo(String name, [ int age ]){
return 'name:$name--age:$age';
}
// 执行函数
print(printUserInfo("小明", 18)) ; // name:小明--age:18
print(printUserInfo("小明")) ; // name:小明--age:null
}
可选参数只能放到方法参数的末尾,不能放到必需参数的前面,否则就会报错
示例:
void main(List args){
// 可选参数name 放在了必须的参数age之前, 就会报错
String printInfo([String name],int age){
return "name:$name---age:$age";
}
}
示例会报错,可选参数不能放在必须的参数之前.
注意, 可选参数在没有传递实参的情况下, 值为null
所以可以通过判断来确定用户是否给可选参数传递了实参,如果没有的话可以自己赋一个默认值
void main(){
// 3\. 可选传参
// 定义函数
String printUserInfo(String name, [ int age ]){
if(age == null){
age= 20;
}
return 'name:$name--age:$age';
}
// 执行函数
print(printUserInfo("小明", 18)) ; // name:小明--age:18
print(printUserInfo("小红")) ; // name:小红--age:20
// 第二次调用就启用了默认值
}
2.4 默认值,
在定义函数的时候,可以使用 =
来定义可选参数的默认值。 默认值只能是编译时常量。 如果没有提供默认值,则默认值为 null。
在形参定义默认值就是使用=
赋值,
void main(){
// 4\. 参数默认值
// 定义函数
String printUserInfo(String name, [ int age , String sex ="男" ]){
if(age == null){
age= 20;
}
return 'name:$name--sex:$sex--age:$age';
}
// 执行函数
print(printUserInfo("小明", 18)) ; // name:小明--sex:男--age:18
print(printUserInfo("小红",)) ; // name:小红--sex:男--age:20
}
在没有传递sex参数的时候,默认为"男",
其实我们可能会注意到, 如果没传递实参,形参就为null, 那么是不是就意味着形参的值只要为null
就会启用默认值,如果你这么想就错了
我们可以显示的传递值为null,来看看
void main(List args){
String printInfo([String name,age = 30]){
return "name:$name---age:$age";
}
print(printInfo("小明",null)); // name:小明---age:null
}
我们会发现,当你显示的传入null以后, 形参的值也是null, 但是却没有启用默认是, 而是使用你复制的null.
注意: 我们在定义形参的时候要注意参数顺序, 上面的例子,如果我想改变传递sex参数,就必须先传递age参数, 如果可选参数调换了位置, 你在传递age参数时,必须先传递sex参数,否则会报错
void main(){
// 4\. 参数默认值
// 定义函数
String printUserInfo(String name, [ String sex ="男",int age ]){
if(age == null){
age= 20;
}
return 'name:$name--sex:$sex--age:$age';
}
// 执行函数
print(printUserInfo("小明", "女",18)) ; // name:小明--sex:女--age:18
print(printUserInfo("小红","不详")) ; // name:小红--sex:不详--age:20
}
这种按照顺序传递的方式, 会有一些不利的情况, 那么我们怎样选择自己想传递的参数呢, 就可以使用命名参数
2.5 命名参数
命名参数就是根据名称匹配你想给那一个形参赋值
命名参数的形参使用大括号进行罗列
void main(){
// 5\. 命名参数
// 定义函数
String printUserInfo(String name, { String sex ="男",int age }){
if(age == null){
age= 20;
}
return 'name:$name--sex:$sex--age:$age';
}
// 执行函数
print(printUserInfo("小明",age:18)) ; // name:小明--sex:男--age:18
print(printUserInfo("小红", sex: "不详")) ; //name:小红--sex:不详--age:20
}
命名参数可以理解也是可选参数的一种, 中括号语法的可选参数, 只能按照顺序赋值,
命名参数就需要你在传递实参的时候带上形参的名字,确定你要给那个形参赋值.
2.6 参数可以是函数
函数可以作为参数传递给另外一个函数
void main(){
// 6.参数函数
getInfo(String info){
print(info); // 就是普通字符串
}
runFn(fn){
fn("就是普通字符串");
}
runFn(getInfo);
}
3.其他
3.1 词法作用域
Dart 是一门词法作用域的编程语言,就意味着变量的作用域是固定的, 简单说变量的作用域在编写代码的时候就已经确定了。 花括号内的是变量可见的作用域。
下面示例关于嵌套函数的变量作用域:
void main(){
getInfo(){
// 当前变量是定义在getInfo 作用域内,
// 变量只能在getInfo作用于中使用
num number = 10;
// 作用域内使用变量, 没有任何问题
print(number); // 10
}
getInfo();
// 在getInfo作用域外调用里面的变量,就会报错
print(number); // 报错
}
3.2 词法闭包
闭包 即一个函数对象,即使函数对象的调用在它原始作用域之外, 依然能够访问在它词法作用域内的变量。
函数可以封闭定义到它作用域内的变量。
下面的示例中, add()
捕获了变量 number
。 无论在什么时候执行返回函数,函数都会使用捕获的 number
变量。
void main(){
getInfo(){
// 当前变量是定义在getInfo 作用域内,
// 变量只能在getInfo作用于中使用
num number = 10;
return (num info){
number += info;
return number;
};
}
Function add = getInfo();
print(add(5));
print(add(1));
print(add(2));
}