Dart: Dart 函数 — 一等公民、闭包与高阶编程

函数是代码的积木 — 小函数组合出大系统。

1. 你将学到


2. 一个开发者的真实故事

(1) 痛点:参数混乱导致调用错误频发

Bob 在 DataPipeline 中定义了 processOrder 函数,有 7 个位置参数。团队成员调用时经常搞混参数顺序 — 有人把 taxRate 传给了 discountRate,导致一批 50,000 笔订单的折扣计算全部错误,客户投诉激增,紧急修复花了 2 天。

(2) 命名参数的解法

Dart 的命名参数 {} 让每个参数都有明确标签,调用时不再依赖顺序,编译器也能检查 required 参数是否提供。

DART
// 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',
);
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

(3) 收益


3. 函数声明与箭头函数

(1) 函数声明语法

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:函数声明方式

DART
// 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);
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
形式 语法 适用场景
标准函数 { return expr; } 多条语句
箭头函数 => expr 单表达式返回
void 函数 void name() {} 无返回值

4. 参数类型详解

(1) 参数决策树

100%
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"]
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:位置参数

DART
// 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
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:命名参数

DART
// 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',
  );
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:required 标记

DART
// 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
}
TEXT 📖 仅展示
> **输出:** 在本地 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) 闭包原理

闭包是一个函数对象,它可以访问其词法作用域中的变量,即使在该作用域之外调用。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:闭包基础

DART
// 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)
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:闭包捕获变量

DART
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
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:闭包在实际场景中的应用

DART
// 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
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

6. 高阶函数

(1) 集合操作高阶函数

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:forEach

DART
void main() {
  final orders = ['ORD-001', 'ORD-002', 'ORD-003'];
  orders.forEach((order) => print('Processing: $order'));
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:map — 转换

DART
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]
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:where — 过滤

DART
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
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:reduce — 聚合

DART
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
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:fold — 带初始值的聚合

DART
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
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
函数 作用 返回类型 需要初始值
forEach 遍历执行 void
map 转换每个元素 Iterable<T>
where 过滤元素 Iterable<T>
reduce 聚合为单值 T 否(但集合不能空)
fold 带初始值聚合 任意

7. 完整示例:DataPipeline 订单分析函数库

DART
// ============================================
// 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);
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

输出:

TEXT 📖 仅展示
=== 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) 更清晰。


📖 小节


📝 作业

  1. 基础题(难度⭐):写一个 formatUSD 函数,用命名参数接受金额和货币符号,返回格式化字符串(如 formatUSD(amount: 1500.5, symbol: '€') 返回 "€1,500.50")。
  2. 进阶题(难度⭐⭐):用闭包实现一个 makeDiscountCalculator 工厂函数,接受折扣率参数,返回一个计算折扣后价格的函数。创建两个不同折扣率的计算器并对比结果。
  3. 挑战题(难度⭐⭐⭐):用 map/where/fold 链式调用,对一组订单数据完成:过滤已完成订单 → 按类别分组 → 计算每类总收入 → 找出收入最高的类别,整个过程不使用任何 for 循环。

← 上一课 | 下一课 →

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏