Dart 函数
函数是组织代码的基本单元,它将一段可复用的逻辑封装起来,需要时调用即可。
Dart 的函数系统非常灵活,支持命名参数、可选参数、箭头函数和高阶函数等特性。
函数定义与返回值
函数由返回类型、函数名、参数列表和函数体组成。
实例
// 定义一个简单的函数
// String 是返回类型,greet 是函数名,(String name) 是参数列表
String greet(String name) {
return '你好,$name!欢迎来到 RUNOOB。';
}
// 没有返回值的函数使用 void
void printWelcome() {
print('=== RUNOOB Dart 教程 ===');
}
void main() {
printWelcome();
// 调用函数并接收返回值
String message = greet('小明');
print(message);
}
// String 是返回类型,greet 是函数名,(String name) 是参数列表
String greet(String name) {
return '你好,$name!欢迎来到 RUNOOB。';
}
// 没有返回值的函数使用 void
void printWelcome() {
print('=== RUNOOB Dart 教程 ===');
}
void main() {
printWelcome();
// 调用函数并接收返回值
String message = greet('小明');
print(message);
}
=== RUNOOB Dart 教程 === 你好,小明!欢迎来到 RUNOOB。
返回值
每个函数都有一个返回值。
如果没有显式 return,函数隐式返回 null(但空安全下这会导致类型不匹配,所以通常用 void 表示无返回值)。
实例
// 返回 int 类型的函数
int add(int a, int b) {
return a + b;
}
// 使用箭头语法简写单表达式函数
int multiply(int a, int b) => a * b;
// void 表示没有有意义的返回值
void log(String msg) {
print('[RUNOOB 日志] $msg');
}
void main() {
print('3 + 5 = ${add(3, 5)}');
print('3 × 5 = ${multiply(3, 5)}');
log('函数学习完成');
}
int add(int a, int b) {
return a + b;
}
// 使用箭头语法简写单表达式函数
int multiply(int a, int b) => a * b;
// void 表示没有有意义的返回值
void log(String msg) {
print('[RUNOOB 日志] $msg');
}
void main() {
print('3 + 5 = ${add(3, 5)}');
print('3 × 5 = ${multiply(3, 5)}');
log('函数学习完成');
}
3 + 5 = 8 3 × 5 = 15 [RUNOOB 日志] 函数学习完成
命名参数与可选参数
Dart 的参数分为两种:必选参数(required)和可选参数。
可选参数又分为命名参数(named)和位置参数(positional)。
命名参数(Named Parameters)
命名参数用花括号 {} 包裹,调用时通过参数名传递,顺序可以任意。
实例
// 花括号 {} 中的是命名参数,默认是可选的
// required 关键字标记为必填
String createUser({
required String name, // 必填的命名参数
int age = 0, // 可选,默认值为 0
String? email, // 可选,可以为 null
bool isVip = false, // 可选,默认值为 false
}) {
var info = '用户名: $name, 年龄: $age';
if (email != null) {
info += ', 邮箱: $email';
}
if (isVip) {
info += ' [VIP用户]';
}
return info;
}
void main() {
// 命名参数:通过参数名传递,顺序无关
print(createUser(name: 'runoob', age: 10));
print(createUser(name: 'admin', email: 'admin@runoob.com', isVip: true));
// 必填参数不能省略
// createUser(); // 错误:缺少必填参数 name
}
// required 关键字标记为必填
String createUser({
required String name, // 必填的命名参数
int age = 0, // 可选,默认值为 0
String? email, // 可选,可以为 null
bool isVip = false, // 可选,默认值为 false
}) {
var info = '用户名: $name, 年龄: $age';
if (email != null) {
info += ', 邮箱: $email';
}
if (isVip) {
info += ' [VIP用户]';
}
return info;
}
void main() {
// 命名参数:通过参数名传递,顺序无关
print(createUser(name: 'runoob', age: 10));
print(createUser(name: 'admin', email: 'admin@runoob.com', isVip: true));
// 必填参数不能省略
// createUser(); // 错误:缺少必填参数 name
}
用户名: runoob, 年龄: 10 用户名: admin, 邮箱: admin@runoob.com [VIP用户]
位置参数(Positional Parameters)
位置参数用方括号 [] 包裹,调用时按顺序传递。
实例
// 方括号 [] 中的是可选位置参数
String buildUrl(String host, [String path = '/', int port = 80]) {
return 'http://$host:$port$path';
}
void main() {
// 只传必填参数,可选参数使用默认值
print(buildUrl('www.runoob.com'));
// 传 2 个参数
print(buildUrl('www.runoob.com', '/dart'));
// 传 3 个参数
print(buildUrl('localhost', '/api', 8080));
}
String buildUrl(String host, [String path = '/', int port = 80]) {
return 'http://$host:$port$path';
}
void main() {
// 只传必填参数,可选参数使用默认值
print(buildUrl('www.runoob.com'));
// 传 2 个参数
print(buildUrl('www.runoob.com', '/dart'));
// 传 3 个参数
print(buildUrl('localhost', '/api', 8080));
}
http://www.runoob.com:80/ http://www.runoob.com:80/dart http://localhost:8080/api
命名参数 vs 位置参数:如何选择
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 参数很多(3个以上) | 命名参数 | 调用时参数名自文档化,不易传错顺序 |
| 参数含义不明显 | 命名参数 | true/false 这类值不说明用途,加名字更清晰 |
| 参数很少且含义明确 | 位置参数 | 如 add(1, 2),简洁明了 |
| Flutter Widget 构建 | 命名参数 | Flutter 标准风格 |
一个函数不能同时使用位置可选参数和命名可选参数。你只能选一种。
默认参数值
可选参数可以指定默认值,当调用者没有传该参数时使用默认值。
实例
// 所有可选参数都设置了默认值
String formatMessage(
String content, {
String prefix = '[RUNOOB]',
String suffix = '',
bool uppercase = false,
}) {
var result = '$prefix $content $suffix';
return uppercase ? result.toUpperCase() : result;
}
void main() {
// 全部使用默认值
print(formatMessage('Dart 教程更新了'));
// 覆盖部分默认值
print(formatMessage('重要通知', prefix: '[公告]', suffix: '!!!'));
// 覆盖默认值 + 开启大写
print(formatMessage('error', prefix: '[错误]', uppercase: true));
}
String formatMessage(
String content, {
String prefix = '[RUNOOB]',
String suffix = '',
bool uppercase = false,
}) {
var result = '$prefix $content $suffix';
return uppercase ? result.toUpperCase() : result;
}
void main() {
// 全部使用默认值
print(formatMessage('Dart 教程更新了'));
// 覆盖部分默认值
print(formatMessage('重要通知', prefix: '[公告]', suffix: '!!!'));
// 覆盖默认值 + 开启大写
print(formatMessage('error', prefix: '[错误]', uppercase: true));
}
[RUNOOB] Dart 教程更新了 [公告] 重要通知 !!! [错误] ERROR
默认值必须是编译时常量。也就是说,默认值不能是 DateTime.now() 或某个函数的返回值(除非该函数是 const 的)。如果需要在运行时确定默认值,可以在函数体内用 ?? 处理。
箭头函数与匿名函数
当函数体只有一条表达式时,可以用箭头语法(=>)简写。
匿名函数是没有名字的函数,通常作为参数传递给其他函数。
实例
void main() {
var numbers = [1, 2, 3, 4, 5];
// 匿名函数(完整写法)
numbers.forEach((number) {
print('RUNOOB 数字: $number');
});
print('---');
// 箭头函数(单表达式简写)
var doubled = numbers.map((n) => n * 2);
print('翻倍: $doubled');
// 箭头函数在函数定义中的使用
// 当函数体只有一个 return 语句时,可以用 => 简写
int square(int x) => x * x;
print('5 的平方: ${square(5)}');
// 存储匿名函数到变量
var sayHello = (String name) => '你好,$name!';
print(sayHello('runoob'));
}
var numbers = [1, 2, 3, 4, 5];
// 匿名函数(完整写法)
numbers.forEach((number) {
print('RUNOOB 数字: $number');
});
print('---');
// 箭头函数(单表达式简写)
var doubled = numbers.map((n) => n * 2);
print('翻倍: $doubled');
// 箭头函数在函数定义中的使用
// 当函数体只有一个 return 语句时,可以用 => 简写
int square(int x) => x * x;
print('5 的平方: ${square(5)}');
// 存储匿名函数到变量
var sayHello = (String name) => '你好,$name!';
print(sayHello('runoob'));
}
RUNOOB 数字: 1 RUNOOB 数字: 2 RUNOOB 数字: 3 RUNOOB 数字: 4 RUNOOB 数字: 5 --- 翻倍: (2, 4, 6, 8, 10) 5 的平方: 25 你好,runoob!
高阶函数与闭包
高阶函数是指可以接收函数作为参数、或将函数作为返回值的函数。
闭包是指函数可以访问其词法作用域中的变量,即使该函数在作用域外被调用。
高阶函数
实例
// 高阶函数:接收一个函数作为参数
List<int> filterList(
List<int> items, bool Function(int) predicate) {
return items.where(predicate).toList();
}
// 高阶函数:返回一个函数
Function makeMultiplier(int factor) {
// 返回一个闭包:记住了外部的 factor
return (int n) => n * factor;
}
void main() {
var numbers = [10, 15, 20, 25, 30];
// 传入一个匿名函数作为筛选条件
var bigNumbers = filterList(numbers, (n) => n > 18);
print('RUNOOB 大于18的数: $bigNumbers');
// 获取返回的函数
var doubleIt = makeMultiplier(2);
var tripleIt = makeMultiplier(3);
print('5 × 2 = ${doubleIt(5)}');
print('5 × 3 = ${tripleIt(5)}');
}
List<int> filterList(
List<int> items, bool Function(int) predicate) {
return items.where(predicate).toList();
}
// 高阶函数:返回一个函数
Function makeMultiplier(int factor) {
// 返回一个闭包:记住了外部的 factor
return (int n) => n * factor;
}
void main() {
var numbers = [10, 15, 20, 25, 30];
// 传入一个匿名函数作为筛选条件
var bigNumbers = filterList(numbers, (n) => n > 18);
print('RUNOOB 大于18的数: $bigNumbers');
// 获取返回的函数
var doubleIt = makeMultiplier(2);
var tripleIt = makeMultiplier(3);
print('5 × 2 = ${doubleIt(5)}');
print('5 × 3 = ${tripleIt(5)}');
}
RUNOOB 大于18的数: [20, 25, 30] 5 × 2 = 10 5 × 3 = 15
闭包(Closure)
闭包是一个函数对象,它可以访问其词法作用域中的变量,即使该函数在原始作用域之外被调用。
实例
// 闭包典型应用:创建计数器
Function makeCounter() {
int count = 0; // 这个变量被返回的函数"捕获"
return () {
count++;
return count;
};
}
void main() {
// counterA 和 counterB 各自拥有独立的 count
var counterA = makeCounter();
var counterB = makeCounter();
print('RUNOOB 计数器 A: ${counterA()}'); // 1
print('RUNOOB 计数器 A: ${counterA()}'); // 2
print('RUNOOB 计数器 B: ${counterB()}'); // 1(独立的计数)
print('RUNOOB 计数器 A: ${counterA()}'); // 3
}
Function makeCounter() {
int count = 0; // 这个变量被返回的函数"捕获"
return () {
count++;
return count;
};
}
void main() {
// counterA 和 counterB 各自拥有独立的 count
var counterA = makeCounter();
var counterB = makeCounter();
print('RUNOOB 计数器 A: ${counterA()}'); // 1
print('RUNOOB 计数器 A: ${counterA()}'); // 2
print('RUNOOB 计数器 B: ${counterB()}'); // 1(独立的计数)
print('RUNOOB 计数器 A: ${counterA()}'); // 3
}
RUNOOB 计数器 A: 1 RUNOOB 计数器 A: 2 RUNOOB 计数器 B: 1 RUNOOB 计数器 A: 3
闭包的关键在于:内部函数"记住"了外部函数的变量,即使外部函数已经执行完毕。每次调用 makeCounter() 都会创建一个全新的 count 变量和闭包,所以多个计数器互不影响。
