Dart 语言速成:从函数闭包到面向对象,30 分钟补齐 Flutter 开发的语言短板(三)
摘要:本文介绍了Dart语言中高阶函数的三种常见用法:函数作为参数、函数作为返回值和闭包。通过银行账户管理示例展示了闭包如何实现私有数据保护(余额只能通过特定接口访问),购物车管理示例演示了闭包维护状态的特性(持久化保存商品信息),以及游戏分数管理示例说明闭包的数据封装能力。这些示例生动体现了闭包在实际开发中的重要应用场景,包括数据安全性、状态保持和封装性等特点,为理解函数式编程提供了实践参考。
·

⚡ 函数类型和高阶函数
// 1. 函数作为参数 - 就像传递工具给别人
typedef MathOperation = int Function(int, int);
int calculate(int a, int b, MathOperation operation) {
return operation(a, b);
}
// 使用不同的操作
int add(int a, int b) => a + b;
int multiply(int a, int b) => a * b;
void functionAsParameter() {
print(calculate(5, 3, add)); // 8
print(calculate(5, 3, multiply)); // 15
// 使用匿名函数
print(calculate(5, 3, (a, b) => a - b)); // 2
}
// 2. 函数作为返回值 - 就像工厂生产工具
Function createMultiplier(int factor) {
return (int number) => number * factor;
}
void functionFactory() {
var double = createMultiplier(2);
var triple = createMultiplier(3);
print(double(5)); // 10
print(triple(5)); // 15
}
// 3. 闭包 - 函数记住外部变量
Function createCounter() {
int count = 0; // 这个变量被"关闭"在函数内
return () {
count++;
return count;
};
}
void closureExample() {
var counter1 = createCounter();
var counter2 = createCounter();
print(counter1()); // 1
print(counter1()); // 2
print(counter2()); // 1 (独立的计数器)
print(counter1()); // 3
}
举例子教你搞懂闭包
🌟 现实生活中的闭包应用场景
场景1: 银行账户管理 - 私有数据保护
Function createBankAccount(double initialBalance) {
double balance = initialBalance; // 私有余额,外部无法直接访问
return (String operation, [double? amount]) {
switch (operation) {
case 'deposit': // 存款
if (amount != null && amount > 0) {
balance += amount;
return '存款成功,余额:¥${balance.toStringAsFixed(2)}';
}
return '存款金额无效';
case 'withdraw': // 取款
if (amount != null && amount > 0 && amount <= balance) {
balance -= amount;
return '取款成功,余额:¥${balance.toStringAsFixed(2)}';
}
return '取款失败:余额不足或金额无效';
case 'balance': // 查询余额
return '当前余额:¥${balance.toStringAsFixed(2)}';
default:
return '无效操作';
}
};
}
//使用示例
void main(){
// 1. 银行账户示例
print('1. 银行账户管理:');
var myAccount = createBankAccount(1000.0);
print(myAccount('balance')); // 查询余额
print(myAccount('deposit', 500.0)); // 存款
print(myAccount('withdraw', 200.0)); // 取款
print(myAccount('withdraw', 2000.0)); // 取款失败
print('');
}
//日志输出
1. 银行账户管理:
当前余额:¥1000.00
存款成功,余额:¥1500.00
取款成功,余额:¥1300.00
取款失败:余额不足或金额无效
场景2: 购物车管理 - 状态保持
Function createShoppingCart() {
List<Map<String, dynamic>> items = []; // 私有购物车数据
return (String action, [Map<String, dynamic>? item]) {
switch (action) {
case 'add':
if (item != null) {
// 检查是否已存在相同商品
int existingIndex = items.indexWhere((i) => i['name'] == item['name']);
if (existingIndex != -1) {
items[existingIndex]['quantity'] += item['quantity'] ?? 1;
} else {
items.add({
'name': item['name'],
'price': item['price'],
'quantity': item['quantity'] ?? 1,
});
}
return '商品已添加到购物车';
}
return '商品信息无效';
case 'remove':
if (item != null) {
items.removeWhere((i) => i['name'] == item['name']);
return '商品已从购物车移除';
}
return '商品信息无效';
case 'list':
if (items.isEmpty) return '购物车为空';
String result = '购物车内容:\n';
for (var item in items) {
result += '- ${item['name']} x${item['quantity']} = ¥${(item['price'] * item['quantity']).toStringAsFixed(2)}\n';
}
return result;
case 'total':
double total = items.fold(0.0, (sum, item) => sum + (item['price'] * item['quantity']));
return '总计:¥${total.toStringAsFixed(2)}';
default:
return '无效操作';
}
};
}
//使用示例
void main(){
// 2. 购物车示例
print('2. 购物车管理:');
var cart = createShoppingCart();
print(cart('add', {'name': '苹果', 'price': 5.0, 'quantity': 3}));
print(cart('add', {'name': '香蕉', 'price': 3.0, 'quantity': 2}));
print(cart('add', {'name': '苹果', 'price': 5.0, 'quantity': 1})); // 增加数量
print(cart('list'));
print(cart('total'));
print('');
}
//日志输出
2. 购物车管理:
商品已添加到购物车
商品已添加到购物车
商品已添加到购物车
购物车内容:
- 苹果 x4 = ¥20.00
- 香蕉 x2 = ¥6.00
总计:¥26.00
场景3: 游戏分数管理 - 数据封装
Function createGameScore(String playerName) {
int score = 0;
int level = 1;
List<String> achievements = [];
return (String action, [dynamic value]) {
switch (action) {
case 'addScore':
if (value is int && value > 0) {
score += value;
// 自动升级逻辑
int newLevel = (score ~/ 1000) + 1;
if (newLevel > level) {
level = newLevel;
achievements.add('达到等级 $level');
return '得分 +$value!升级到等级 $level!';
}
return '得分 +$value!当前分数:$score';
}
return '无效分数';
case 'getInfo':
return {
'player': playerName,
'score': score,
'level': level,
'achievements': List.from(achievements),
};
case 'reset':
score = 0;
level = 1;
achievements.clear();
return '游戏数据已重置';
default:
return '无效操作';
}
};
}
//使用示例
void main(){
// 3. 游戏分数示例
print('3. 游戏分数管理:');
var playerScore = createGameScore('小明');
print(playerScore('addScore', 800));
print(playerScore('addScore', 300)); // 升级
print(playerScore('addScore', 500));
print('玩家信息:${playerScore('getInfo')}');
print('');
}
//日志输出
3. 游戏分数管理:
得分 +800!当前分数:800
得分 +300!升级到等级 2!
得分 +500!当前分数:1600
玩家信息:{player: 小明, score: 1600, level: 2, achievements: [达到等级 2]}
场景4: 定时器工厂 - 配置保持
Function createTimer(Duration interval) {
int tickCount = 0;
bool isRunning = false;
return (String command) {
switch (command) {
case 'start':
if (!isRunning) {
isRunning = true;
return '定时器已启动,间隔:${interval.inSeconds}秒';
}
return '定时器已在运行';
case 'tick':
if (isRunning) {
tickCount++;
return '第${tickCount}次触发 (${DateTime.now().toString().substring(11, 19)})';
}
return '定时器未启动';
case 'stop':
isRunning = false;
return '定时器已停止,总共触发${tickCount}次';
case 'reset':
tickCount = 0;
isRunning = false;
return '定时器已重置';
default:
return '无效命令';
}
};
}
//使用示例
void main(){
// 4. 定时器示例
print('4. 定时器管理:');
var timer = createTimer(Duration(seconds: 1));
print(timer('start'));
print(timer('tick'));
print(timer('tick'));
print(timer('tick'));
print(timer('stop'));
}
//日志输出
4. 定时器管理:
定时器已启动,间隔:1秒
第1次触发 (13:05:37)
第2次触发 (13:05:37)
第3次触发 (13:05:37)
定时器已停止,总共触发3次
💡 什么时候使用闭包?
闭包的核心价值:数据封装 + 状态保持
🎯 使用闭包的场景:
-
数据私有化 - 需要隐藏内部数据,只通过特定接口访问
- 银行账户:余额不能直接修改,只能通过存取款操作
- 用户权限:权限数据私有,只能通过验证接口访问
-
状态保持 - 需要在多次调用间保持状态
- 购物车:商品列表在多次操作间保持
- 游戏分数:分数、等级在游戏过程中累积
-
配置保持 - 创建时设置配置,后续使用时保持不变
- 定时器:创建时设置间隔,使用时保持该配置
- 数据库连接:创建时设置连接参数,使用时保持连接
-
工厂模式 - 根据参数创建不同行为的函数
- 创建不同倍数的乘法器
- 创建不同规则的验证器
-
回调函数 - 需要携带上下文信息的回调
- 事件处理:记住触发时的环境信息
- 异步操作:记住操作开始时的状态
🚫 不适合使用闭包的场景:
- 简单计算 - 纯函数计算,不需要状态
- 一次性操作 - 不需要保持状态的操作
- 全局状态 - 需要在整个应用间共享的状态
- 复杂对象 - 复杂的数据结构应该用类来管理
💡 记忆要点:
- 闭包 = 函数 + 它能访问的外部变量
- 主要用于:数据封装、状态保持、配置保持
- 类比:就像一个带有私人保险箱的服务员,只有他知道密码,但可以帮你操作
🤔 为什么闭包中的变量会保持住?
让我用生活中的场景来详细解释这个重要概念:
场景1:🏠 搬家的故事
想象你要搬家,但有一个特殊的房间需要保留:
Function createHousekeeper() {
String secretCode = "12345"; // 这是房间的密码
int visitCount = 0; // 访问次数记录
return (String action) {
visitCount++; // 每次调用都会增加
if (action == "enter") {
return "用密码$secretCode进入房间,这是第${visitCount}次访问";
}
return "无效操作";
};
}
// 正常情况下,函数执行完,局部变量就消失了
// 但是闭包不一样!
var housekeeper = createHousekeeper();
print(housekeeper("enter")); // 用密码12345进入房间,这是第1次访问
print(housekeeper("enter")); // 用密码12345进入房间,这是第2次访问
🔍 为什么secretCode和visitCount没有消失?
在正常情况下:
- createHousekeeper()函数执行完毕
- 函数内的局部变量secretCode和visitCount应该被销毁
- 内存应该被回收
但是闭包的魔法在于:
- 返回的函数还在"引用"这些变量
- Dart发现:“哦,这些变量还有人在用,不能删除!”
- 所以这些变量被"保护"起来,继续存在
🧠 技术原理简化解释:
正常情况:
函数执行 → 创建局部变量 → 函数结束 → 变量销毁 → 内存回收
闭包情况:
函数执行 → 创建局部变量 → 返回内部函数 → 内部函数引用局部变量 →
变量不能销毁(还有引用) → 变量保持存在 → 形成闭包
💡 关键理解:
- 引用保护:内部函数持有对外部变量的引用,防止被垃圾回收
- 独立空间:每次调用外部函数都创建新的变量空间
- 访问控制:只有内部函数能访问这些变量,实现数据封装
- 状态持久:变量在多次调用间保持状态
🎯 生活类比总结:
- 闭包 = 有记忆的服务员
- 外部变量 = 服务员的小本本(记录信息)
- 内部函数 = 服务员提供的服务
- 变量保持 = 小本本不会丢失,一直跟着服务员
这就是为什么闭包中的变量会保持住的原因!*/
*如果文章对您有帮助,麻烦动动发财的小手,点赞、关注和收藏,将是作者不断更新的动力🙏🏻*
更多推荐



所有评论(0)