dart学习第 5 节:函数基础 —— 代码复用的最小单位

86 阅读3分钟

今天我们要学习 Dart 中代码复用的最小单位 ——函数。函数就像一个个 “代码模块”,能把重复使用的逻辑封装起来,让程序更简洁、更易维护。

一、函数定义:基本结构与规范

函数是执行特定任务的代码块,在 Dart 中,函数的定义需要明确返回值类型函数名参数列表函数体

1. 基本语法

返回值类型 函数名(参数列表) {
  // 函数体(执行的代码)
  return 返回值; // 与返回值类型匹配
}

2. 详细说明

  • 返回值类型

    • 若函数有返回值,需指定具体类型(如 intString 等)。
    • 若没有返回值,可省略或指定为 void
  • 函数名

    • 遵循小驼峰命名法(首字母小写,后续单词首字母大写),如 calculateSumprintMessage
    • 命名需清晰表达函数功能,避免模糊的名称(如 doSomething)。
  • 参数列表

    • 由 “参数类型 参数名” 组成,多个参数用逗号分隔。
    • 是函数接收外部数据的入口。

示例 1:有返回值的函数

// 定义一个计算两个整数之和的函数
int calculateSum(int a, int b) {
  int result = a + b;
  return result; // 返回计算结果(int类型)
}

void main() {
  int sum = calculateSum(3, 5); // 调用函数,传入参数3和5
  print("和为:$sum"); // 输出:和为:8
}

示例 2:无返回值的函数(void)

// 定义一个打印信息的函数
void printMessage(String name) {
  print("Hello, $name!"); // 没有return语句
}

void main() {
  printMessage("Dart"); // 调用函数,传入参数"Dart"
  // 输出:Hello, Dart!
}

3. 函数的简化写法

当函数体只有一行返回语句时,可省略大括号和 return,直接用箭头 => 连接:

// 简化版的求和函数
int calculateSum(int a, int b) => a + b;

// 简化版的打印函数(void函数也可简化)
void printMessage(String name) => print("Hello, $name!");

二、可选参数:让函数更灵活

在某些场景下,函数的参数可能不是必须的(比如可选的配置项)。Dart 提供了两种可选参数:位置可选参数命名可选参数

1. 位置可选参数([] 包裹)

  • 用方括号 [] 包裹参数,放在必选参数之后。
  • 调用时可省略,若省略则使用默认值(若未指定默认值,则为 null)。
  • 传递参数时需按位置顺序对应。

示例:带默认值的位置可选参数

// 定义一个计算商品总价的函数:单价(必选)+ 数量(可选,默认1)+ 折扣(可选,默认0)
double calculateTotal(double price, [int count = 1, double discount = 0.0]) {
  double total = price * count * (1 - discount);
  return total;
}

void main() {
  // 情况1:只传必选参数(单价)
  double total1 = calculateTotal(100.0);
  print("总价1:$total1"); // 输出:总价1:100.0(数量1,无折扣)

  // 情况2:传必选参数+第一个可选参数(数量)
  double total2 = calculateTotal(100.0, 2);
  print("总价2:$total2"); // 输出:总价2:200.0(数量2,无折扣)

  // 情况3:传所有参数
  double total3 = calculateTotal(100.0, 2, 0.1);
  print("总价3:$total3"); // 输出:总价3:180.0(数量2,9折)
}

2. 命名可选参数({} 包裹)

  • 用大括号 {} 包裹参数,通常指定默认值(否则可能为 null)。
  • 调用时需用 “参数名:值” 的形式传递,不依赖位置顺序
  • 更适合参数较多的场景,提高代码可读性。

示例:命名可选参数

// 定义一个创建用户信息的函数:姓名(必选)+ 年龄(可选)+ 邮箱(可选)
String createUser(String name, {int? age, String? email}) {
  String info = "姓名:$name";
  if (age != null) info += ",年龄:$age";
  if (email != null) info += ",邮箱:$email";
  return info;
}

void main() {
  // 情况1:只传必选参数
  String user1 = createUser("张三");
  print(user1); // 输出:姓名:张三

  // 情况2:传必选参数+邮箱(跳过年龄)
  String user2 = createUser("李四", email: "lisi@example.com");
  print(user2); // 输出:姓名:李四,邮箱:lisi@example.com

  // 情况3:传所有参数(顺序无关)
  String user3 = createUser("王五", age: 25, email: "wangwu@example.com");
  print(user3); // 输出:姓名:王五,年龄:25,邮箱:wangwu@example.com
}

小贴士:实际开发中,命名可选参数更常用,因为参数名明确,不易出错。



三、匿名函数:没有名字的函数

匿名函数就是没有函数名的函数,通常用于临时定义简单逻辑,作为参数传递给其他函数。

1. 基本语法

(参数列表) {
  // 函数体
};
// 或简化为(单行时)
(参数列表) => 表达式;

2. 常见用法:作为参数传递

在集合的 forEachmap 等方法中,经常使用匿名函数:

示例 1:遍历集合时使用匿名函数

void main() {
  List<String> fruits = ['苹果', '香蕉', '橙子'];

  // 用匿名函数作为forEach的参数
  fruits.forEach((fruit) {
    print("水果:$fruit");
  });
  // 输出:
  // 水果:苹果
  // 水果:香蕉
  // 水果:橙子
}

示例 2:临时计算逻辑

void main() {
  // 定义一个接收函数作为参数的函数
  void calculateAndPrint(int a, int b, Function operation) {
    dynamic result = operation(a, b); // 调用传入的函数
    print("结果:$result");
  }

  // 传递匿名函数(加法)
  calculateAndPrint(5, 3, (x, y) => x + y); // 输出:结果:8

  // 传递另一个匿名函数(乘法)
  calculateAndPrint(5, 3, (x, y) => x * y); // 输出:结果:15
}


四、立即执行函数:定义后马上运行

立即执行函数(IIFE,Immediately Invoked Function Expression)是定义后立即调用的函数,通常用于隔离作用域(避免变量污染)。

1. 语法与示例

在函数定义后直接加 () 传入参数,即可立即执行:

void main() {
  // 立即执行函数(无参数)
  (() {
    print("这个函数会立即执行!");
  })();
  // 输出:这个函数会立即执行!

  // 带参数的立即执行函数
  ((String message) {
    print("消息:$message");
  })("Hello, IIFE!");
  // 输出:消息:Hello, IIFE!

  // 立即执行函数的返回值可以被接收
  int result = ((int a, int b) => a + b)(10, 20);
  print("立即计算的和:$result"); // 输出:立即计算的和:30
}

2. 作用:隔离变量作用域

立即执行函数内部的变量不会影响外部:

void main() {
  // 外部变量
  int x = 10;

  // 立即执行函数
  (() {
    int x = 20; // 内部变量,与外部x无关
    print("内部x:$x"); // 输出:内部x:20
  })();

  print("外部x:$x"); // 输出:外部x:10(不受内部影响)
}

五、函数的其他特性

1. 函数作为返回值

函数可以作为另一个函数的返回值,这在高阶函数中很常见:

// 定义一个返回函数的函数
Function makeAdder(int increment) {
  return (int number) => number + increment; // 返回一个匿名函数
}

void main() {
  // 获取一个“加5”的函数
  var add5 = makeAdder(5);
  print(add5(10)); // 输出:15(10 + 5)

  // 获取一个“加10”的函数
  var add10 = makeAdder(10);
  print(add10(10)); // 输出:20(10 + 10)
}

2. 函数参数的类型限制

为了增强类型安全,可指定函数参数的类型(如 int Function(int, int) 表示接收两个 int 参数并返回 int 的函数):

// 定义一个接收特定类型函数的函数
int calculate(int a, int b, int Function(int, int) operation) {
  return operation(a, b);
}

void main() {
  // 传递符合类型要求的函数(加法)
  int sum = calculate(3, 4, (x, y) => x + y);
  print(sum); // 输出:7

  // 传递乘法函数
  int product = calculate(3, 4, (x, y) => x * y);
  print(product); // 输出:12
}