深入浅出 Dart: 学习函数的基本使用方法

2,224 阅读4分钟

介绍

函数是可读,可维护和可重用代码的构建块。 函数是一组用于执行特定任务的语句。 函数将程序组织成逻辑代码块。 一旦定义,可以调用函数来访问代码。 这使得代码可以重用。 此外,函数可以轻松读取和维护程序的代码。

函数这块的话没啥好讲的,我主要通过例子的方式来跟大家讲解,相信大家看了就会明白,不明白的可以直接留言,我会及时回复的

函数

普通函数与 Lambda 函数的定义的方式

// 推荐
int add(int x, int y) {
    return x + y;
}

// 函数定义可省略类型,但并不推荐
add1(x, y) {
    return x + y;
}

// 当函数体内只有一个表达式的时候可以使用:
//语法 [return_type]function_name(parameters)=>expression;
int add2(int x, int y) => x + y;

print(add(1, 2));

注意:所有的函数都返回一个值。如果没有指定返回值,则 默认把语句 return null; 作为函数的最后一个语句执行

可选参数

可选命名参数:使用 {param1, param2, …} 的形式来指定命名参数

int add({int x, int y}) {
    x ??= 1;
    y ??= 2;
    return x + y;
}

print(add(x:2, y:4)); 	// 6
print(add()); 	 		// 3;

在这里我们在函数体内 x ??= 1 是因为这个函数是可选参数,如果没有这个参数我们直接 return x + y 会报错的

提示很明确: 无法读取null的属性,大家需要多多注意啊

可选位置参数:把可选参数放到 [] 中,必填参数要放在可选参数前面

int add(int x, [int y, int z]) {
	y ??= 2;
	z ??= 3;
	return x + y + z;
}

print(add(1));   		// 6
print(add(1, 3, 4));	// 8

可选命名参数默认值(默认值必须是编译时常量), 目前可以使用等号 '=' 或冒号 ':'

int add(int x, {int y = 2, int z = 3}) => x + y + z;

print(add(1));  		// 6
print(add(1, y: 5)); 	// 9

Dart SDK 1.21 之前只能用冒号,冒号的支持以后会移除,所以建议使用等号

可选位置参数默认值(默认值必须是编译时常量),只能使用等号 '='

int add(int x, [int y = 2, int z = 3]) => x + y + z;

print(add(1));			// 6
print(add(1, 3, 5)); 	// 9

匿名函数

匿名函数的参照如下:

([[Type] param1[, …]]) {
	codeBlock;
};

匿名函数,顾名思义就是没有名字的函数

无参匿名函数:

var info = () => print('这是无参匿名函数');
info();  // 这是无参数匿名函数

有参匿名函数:

var info = (name) => 'I am $name';
print(info('gaozan')); // I am gaozan

List.forEach() 就用的匿名函数

List list = [1, 2, 3];
list.forEach((item) => print('$item'));

通常不希望再次使用(即只使用一次的)的函数可以定义为匿名函数

扩展: 如果还是想再次使用匿名函数的话, 也有方法, 像我们上方的例子一样。可赋值给变量,通过变量调用

闭包

  • 闭包是一个方法(对象)

  • 闭包定义在其他方法内部

  • 闭包能够访问外部方法内的局部变量,并持有其状态(这是闭包最大的作用,可以通过闭包的方式,将其暴露出去,提供给外部访问)

返回Function对象(闭包)下面有一个例子:

Function makeAddFunc(int x) {
  x++;
  return (int y) => x + y;
}

main() {
  var addFunc1 = makeAddFunc(2);
  var addFunc2 = makeAddFunc(4);
  print(addFunc1(3));  // 6
  print(addFunc2(3));  // 8
}

...

// 大家可以先想一想结果为什么是 6 和 8 ?

画图一向丑,不要介意。不明白直接留言,知无不言

函数别名

普通的函数定义。在赋值之后,会丢失函数签名信息。

使用typedef 给函数起一个别名,使用比较方便。例如定义一个方法的回调,直接使用别名定义

void main() {
  // 函数别名
  MyAlias myAlias;
  // 可以指向任何同签名的函数
  myAlias = subtsract;
  myAlias(4, 2);		// subtsract: 2
  myAlias = divide;
  myAlias(4, 2);		// divide: 2
    
  // typedef 作为参数传递给函数	
  calculator(4, 2, subtsract); // subtsract: 2
}

// 函数别名
typedef MyAlias(int a, int b);
// 根据MyAlias相同的函数签名定义两个函数
subtsract(int a, int b) {
  print('subtsract: ${a - b}');
}

divide(int a, int b) {
  print('divide: ${a / b}');
}

// typedef 也可以作为参数传递给函数
calculator(int a, int b, MyAlias func) {
  func(a, b);
}

再来个好玩的,我就不讲了哈,Demo3 大家自己玩一玩,可以贴到留言里哈

void main() {          // 主入口
 Fun1 fun1;
  
 fun1 = add;
  
 Demo1(fun1, 1, 2);     // sum1 = 3
 Demo2(fun1, 1, 2);     // sum2 = 3
 // Demo3() ...         ???
}

typedef Fun1(int a, int b);
typedef Fun2<T, K>(T a, K b);

int add(int a, int b) {
  print('a + b');
  return a + b;
}

Demo1(int f(int a, int b), int x, int y) {
  var sum = f(x, y);
  print("sum1 = $sum");
}

Demo2(Fun1 f, int x, int y) {
  var sum = f(x, y);
  print("sum2 = $sum");
}

Demo3(Fun2<int, int> f, int x, int y) {
  var sum = f(x, y);
  print("sum3 = $sum");
}

函数递归

递归这个没啥好讲的,跟其他语言都差不多,留一个例子,通过方法的递归求和

main() {
  var sum = 0;
  fn(int n) {
    sum += n;
    if (n == 0) {
      return;
    }
    fn(n - 1);
  }

  fn(100);
  print(sum);
}