Dart: Dart 函数 — 一等公民、闭包与高阶编程
函数是代码的积木 — 小函数组合出大系统。
1. 你将学到
- 函数声明与箭头函数(
=>语法) - 可选参数:命名参数
{}与位置参数[] - 必传参数标记
required - 闭包与变量捕获机制
- 高阶函数:forEach / map / where / reduce / fold
2. 一个开发者的真实故事
(1) 痛点:参数混乱导致调用错误频发
Bob 在 DataPipeline 中定义了 processOrder 函数,有 7 个位置参数。团队成员调用时经常搞混参数顺序 — 有人把 taxRate 传给了 discountRate,导致一批 50,000 笔订单的折扣计算全部错误,客户投诉激增,紧急修复花了 2 天。
(2) 命名参数的解法
Dart 的命名参数 {} 让每个参数都有明确标签,调用时不再依赖顺序,编译器也能检查 required 参数是否提供。
// Before: positional params - easy to mix up
// processOrder('ORD-001', 1500.0, 0.08, 0.1, true, 'USD', 'json');
// After: named params - self-documenting
processOrder(
id: 'ORD-001',
amount: 1500.0,
taxRate: 0.08,
discountRate: 0.1,
currency: 'USD',
);
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
(3) 收益
- 命名参数让函数调用自文档化,不再需要查定义来确认参数含义
- required 标记让编译器帮你检查必传参数
- 高阶函数让集合操作更简洁,减少 70% 的 for 循环
3. 函数声明与箭头函数
(1) 函数声明语法
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:函数声明方式
// Standard function declaration
double calculateTax(double amount, double taxRate) {
return amount * taxRate;
}
// Arrow function (expression body)
double calculateTaxShort(double amount, double taxRate) =>
amount * taxRate;
// Void function
void printOrderSummary(String id, double amount) {
print('Order $id: \$${amount.toStringAsFixed(2)} USD');
}
// Main entry
void main() {
print(calculateTax(1500.0, 0.08)); // 120.0
print(calculateTaxShort(1500.0, 0.08)); // 120.0
printOrderSummary('ORD-001', 1500.0);
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
| 形式 | 语法 | 适用场景 |
|---|---|---|
| 标准函数 | { return expr; } |
多条语句 |
| 箭头函数 | => expr |
单表达式返回 |
| void 函数 | void name() {} |
无返回值 |
4. 参数类型详解
(1) 参数决策树
graph TD A[Function Params] --> B[Positional<br/>required by default] A --> C[Named<br/>optional by default] B --> B1[Required positional] B --> B2["Optional positional []"] C --> C1["Required named required"] C --> C2["Optional named with default"]
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:位置参数
// Required positional parameters
double calculateTotal(double amount, double taxRate) {
return amount * (1 + taxRate);
}
// Optional positional parameters
String formatCurrency(double amount, [String currency = 'USD']) {
return '\$${amount.toStringAsFixed(2)} $currency';
}
void main() {
print(calculateTotal(1500.0, 0.08)); // 1620.0
print(formatCurrency(1500.0)); // $1500.00 USD
print(formatCurrency(1500.0, 'EUR')); // $1500.00 EUR
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:命名参数
// Named parameters - optional by default
double processOrder({
required String id,
required double amount,
double taxRate = 0.08,
double discountRate = 0,
String currency = 'USD',
}) {
final discounted = amount * (1 - discountRate);
final taxed = discounted * (1 + taxRate);
print('Order $id: \$${taxed.toStringAsFixed(2)} $currency');
return taxed;
}
void main() {
processOrder(id: 'ORD-001', amount: 1500.0);
processOrder(
id: 'ORD-002',
amount: 3200.0,
taxRate: 0.10,
discountRate: 0.15,
currency: 'EUR',
);
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:required 标记
// required forces the caller to provide the parameter
class OrderValidator {
bool validate({
required String orderId,
required double amount,
String? customerName, // Optional - can be null
}) {
if (orderId.isEmpty) return false;
if (amount <= 0) return false;
return true;
}
}
void main() {
final validator = OrderValidator();
// validator.validate(orderId: 'ORD-001'); // Error! amount is required
validator.validate(orderId: 'ORD-001', amount: 1500.0); // OK
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
| 参数类型 | 语法 | 默认必传 | 默认值 |
|---|---|---|---|
| 位置参数 | Type name |
是 | 无 |
| 可选位置参数 | [Type name = default] |
否 | 可指定 |
| 命名参数 | {Type name} |
否 | null |
| required 命名 | {required Type name} |
是 | 无 |
| 带默认值命名 | {Type name = default} |
否 | 指定值 |
5. 闭包与变量捕获
(1) 闭包原理
闭包是一个函数对象,它可以访问其词法作用域中的变量,即使在该作用域之外调用。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:闭包基础
// Function that returns a function (closure)
Function makeTaxCalculator(double taxRate) {
// taxRate is captured by the returned function
return (double amount) => amount * taxRate;
}
void main() {
final usTax = makeTaxCalculator(0.08);
final euTax = makeTaxCalculator(0.20);
print(usTax(1500.0)); // 120.0 (8% tax)
print(euTax(1500.0)); // 300.0 (20% tax)
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:闭包捕获变量
void main() {
// Counter closure
int Function() makeCounter() {
int count = 0; // Captured variable
return () => ++count;
}
final counterA = makeCounter();
final counterB = makeCounter();
print(counterA()); // 1
print(counterA()); // 2
print(counterB()); // 1 (separate captured variable)
print(counterA()); // 3
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:闭包在实际场景中的应用
// DataPipeline filter factory
typedef OrderFilter = bool Function(Map<String, dynamic> order);
OrderFilter makeAmountFilter(double minAmount, {double? maxAmount}) {
return (order) {
final amount = order['amount'] as double;
if (amount < minAmount) return false;
if (maxAmount != null && amount > maxAmount) return false;
return true;
};
}
void main() {
final orders = [
{'id': 'ORD-001', 'amount': 1500.0},
{'id': 'ORD-002', 'amount': 50.0},
{'id': 'ORD-003', 'amount': 3200.0},
];
final premiumFilter = makeAmountFilter(1000);
final midRangeFilter = makeAmountFilter(100, maxAmount: 1000);
print(orders.where(premiumFilter).length); // 2
print(orders.where(midRangeFilter).length); // 1
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
6. 高阶函数
(1) 集合操作高阶函数
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:forEach
void main() {
final orders = ['ORD-001', 'ORD-002', 'ORD-003'];
orders.forEach((order) => print('Processing: $order'));
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:map — 转换
void main() {
final amounts = [1500.0, 3200.0, 890.0];
// Transform amounts to formatted strings
final formatted = amounts.map((a) => '\$${a.toStringAsFixed(2)} USD').toList();
print(formatted); // [$1500.00 USD, $3200.00 USD, $890.00 USD]
// Apply tax calculation
final withTax = amounts.map((a) => a * 1.08).toList();
print(withTax); // [1620.0, 3456.0, 961.2]
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:where — 过滤
void main() {
final orders = [
{'id': 'ORD-001', 'amount': 1500.0, 'status': 'completed'},
{'id': 'ORD-002', 'amount': 50.0, 'status': 'completed'},
{'id': 'ORD-003', 'amount': 3200.0, 'status': 'pending'},
];
// Filter high-value completed orders
final premiumCompleted = orders
.where((o) => o['status'] == 'completed')
.where((o) => (o['amount'] as double) >= 1000)
.toList();
print('Premium completed: ${premiumCompleted.length}'); // 1
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:reduce — 聚合
void main() {
final amounts = [1500.0, 3200.0, 890.0];
// Sum all amounts
final total = amounts.reduce((sum, amount) => sum + amount);
print('Total: \$${total.toStringAsFixed(2)} USD'); // $5590.00 USD
// Find maximum
final maxAmount = amounts.reduce((max, amount) => amount > max ? amount : max);
print('Max: \$${maxAmount.toStringAsFixed(2)} USD'); // $3200.00 USD
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:fold — 带初始值的聚合
void main() {
final orders = [
{'amount': 1500.0, 'status': 'completed'},
{'amount': 3200.0, 'status': 'pending'},
{'amount': 890.0, 'status': 'completed'},
];
// fold with initial value and accumulator
final completedRevenue = orders.fold``<double>``(0.0, (sum, order) {
if (order['status'] == 'completed') {
return sum + (order['amount'] as double);
}
return sum;
});
print('Completed revenue: \$${completedRevenue.toStringAsFixed(2)} USD');
// Completed revenue: $2390.00 USD
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
| 函数 | 作用 | 返回类型 | 需要初始值 |
|---|---|---|---|
forEach |
遍历执行 | void | 否 |
map |
转换每个元素 | Iterable<T> |
否 |
where |
过滤元素 | Iterable<T> |
否 |
reduce |
聚合为单值 | T | 否(但集合不能空) |
fold |
带初始值聚合 | 任意 | 是 |
7. 完整示例:DataPipeline 订单分析函数库
// ============================================
// DataPipeline Order Analysis Function Library
// Demonstrates functions, closures, and higher-order functions
// ============================================
typedef Order = Map<String, dynamic>;
// Filter factory using closures
bool Function(Order) makeFilter({
double? minAmount,
String? requiredStatus,
}) {
return (Order order) {
if (minAmount != null && (order['amount'] as double) < minAmount) {
return false;
}
if (requiredStatus != null && order['status'] != requiredStatus) {
return false;
}
return true;
};
}
// Aggregation function with fold
Map<String, double> aggregateByCategory(List``<Order>`` orders) {
return orders.fold<Map<String, double>>({}, (acc, order) {
final category = (order['category'] ?? 'Uncategorized') as String;
final amount = order['amount'] as double;
acc[category] = (acc[category] ?? 0) + amount;
return acc;
});
}
// Format report entry
String formatEntry(String category, double amount) =>
' $category: \$${amount.toStringAsFixed(2)} USD';
// Main pipeline function
void runPipeline(List``<Order>`` orders, {double minAmount = 0}) {
final filter = makeFilter(minAmount: minAmount, requiredStatus: 'completed');
final filtered = orders.where(filter).toList();
final total = filtered.fold``<double>``(
0, (sum, o) => sum + (o['amount'] as double));
final byCategory = aggregateByCategory(filtered);
print('=== DataPipeline Report ===');
print('Total orders: ${orders.length}');
print('Filtered: ${filtered.length} (min: \$${minAmount} USD)');
print('Revenue: \$${total.toStringAsFixed(2)} USD');
print('\nBy Category:');
byCategory.forEach((cat, amount) => print(formatEntry(cat, amount)));
}
void main() {
final orders = ``<Order>``[
{'id': 'ORD-001', 'amount': 1500.0, 'status': 'completed', 'category': 'Electronics'},
{'id': 'ORD-002', 'amount': 50.0, 'status': 'completed', 'category': 'Books'},
{'id': 'ORD-003', 'amount': 3200.0, 'status': 'pending', 'category': 'Electronics'},
{'id': 'ORD-004', 'amount': 890.0, 'status': 'completed', 'category': 'Clothing'},
{'id': 'ORD-005', 'amount': 2100.0, 'status': 'completed', 'category': 'Electronics'},
];
runPipeline(orders, minAmount: 100);
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
输出:
=== DataPipeline Report ===
Total orders: 5
Filtered: 3 (min: $100 USD)
Revenue: $4490.00 USD
By Category:
Electronics: $3600.00 USD
Clothing: $890.00 USD
❓ 常见问题
Q:命名参数和位置参数可以混用吗? A:可以,但命名参数必须放在位置参数后面。如
void f(int a, {int? b})合法,void f({int? b}, int a)不合法。
Q:箭头函数和普通函数有性能差异吗? A:没有。箭头函数只是语法糖,编译后与普通函数完全相同。选择取决于代码可读性。
Q:reduce 和 fold 的区别是什么? A:reduce 不需要初始值,但集合不能为空,返回类型与元素相同;fold 需要初始值,可以为空集合,返回类型可以不同。推荐优先用 fold。
Q:闭包捕获的变量是值还是引用? A:Dart 闭包捕获的是变量的引用(不是值的副本),所以闭包内修改会影响外部变量,外部修改也影响闭包。
Q:高阶函数 map/where 返回的是 List 还是 Iterable? A:返回 Iterable(惰性求值)。需要 List 时加 .toList()。惰性求值意味着 map/where 链只在迭代时才计算,不会创建中间集合。
Q:函数可以作为参数传递给任何函数吗? A:是的,Dart 函数是一等公民,可以赋值给变量、作为参数传递、作为返回值。
Q:typedef 有什么用? A:typedef 为函数类型创建别名,使代码更可读。如
typedef Validator = bool Function(String);比bool Function(String)更清晰。
📖 小节
- Dart 函数是一等公民:可赋值、传参、返回
- 命名参数 + required 让函数调用自文档化,避免参数混淆
- 闭包捕获外部变量的引用,可创建工厂函数和配置化过滤器
- 高阶函数 map/where/reduce/fold 是集合操作的核心工具
- 函数参数决策:必传用 required 命名参数,可选用带默认值的命名参数
📝 作业
- 基础题(难度⭐):写一个
formatUSD函数,用命名参数接受金额和货币符号,返回格式化字符串(如formatUSD(amount: 1500.5, symbol: '€')返回"€1,500.50")。 - 进阶题(难度⭐⭐):用闭包实现一个
makeDiscountCalculator工厂函数,接受折扣率参数,返回一个计算折扣后价格的函数。创建两个不同折扣率的计算器并对比结果。 - 挑战题(难度⭐⭐⭐):用 map/where/fold 链式调用,对一组订单数据完成:过滤已完成订单 → 按类别分组 → 计算每类总收入 → 找出收入最高的类别,整个过程不使用任何 for 循环。