在这里插入图片描述

⚡ 函数类型和高阶函数

// 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. 数据私有化 - 需要隐藏内部数据,只通过特定接口访问

    • 银行账户:余额不能直接修改,只能通过存取款操作
    • 用户权限:权限数据私有,只能通过验证接口访问
  2. 状态保持 - 需要在多次调用间保持状态

    • 购物车:商品列表在多次操作间保持
    • 游戏分数:分数、等级在游戏过程中累积
  3. 配置保持 - 创建时设置配置,后续使用时保持不变

    • 定时器:创建时设置间隔,使用时保持该配置
    • 数据库连接:创建时设置连接参数,使用时保持连接
  4. 工厂模式 - 根据参数创建不同行为的函数

    • 创建不同倍数的乘法器
    • 创建不同规则的验证器
  5. 回调函数 - 需要携带上下文信息的回调

    • 事件处理:记住触发时的环境信息
    • 异步操作:记住操作开始时的状态

🚫 不适合使用闭包的场景:

  1. 简单计算 - 纯函数计算,不需要状态
  2. 一次性操作 - 不需要保持状态的操作
  3. 全局状态 - 需要在整个应用间共享的状态
  4. 复杂对象 - 复杂的数据结构应该用类来管理

💡 记忆要点:

  • 闭包 = 函数 + 它能访问的外部变量
  • 主要用于:数据封装、状态保持、配置保持
  • 类比:就像一个带有私人保险箱的服务员,只有他知道密码,但可以帮你操作

🤔 为什么闭包中的变量会保持住?

让我用生活中的场景来详细解释这个重要概念:

场景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发现:“哦,这些变量还有人在用,不能删除!”
  • 所以这些变量被"保护"起来,继续存在

🧠 技术原理简化解释:

正常情况:

函数执行 → 创建局部变量 → 函数结束 → 变量销毁 → 内存回收

闭包情况:

函数执行 → 创建局部变量 → 返回内部函数 → 内部函数引用局部变量 → 
变量不能销毁(还有引用) → 变量保持存在 → 形成闭包

💡 关键理解:

  1. 引用保护:内部函数持有对外部变量的引用,防止被垃圾回收
  2. 独立空间:每次调用外部函数都创建新的变量空间
  3. 访问控制:只有内部函数能访问这些变量,实现数据封装
  4. 状态持久:变量在多次调用间保持状态

🎯 生活类比总结:

  • 闭包 = 有记忆的服务员
  • 外部变量 = 服务员的小本本(记录信息)
  • 内部函数 = 服务员提供的服务
  • 变量保持 = 小本本不会丢失,一直跟着服务员

这就是为什么闭包中的变量会保持住的原因!*/




*如果文章对您有帮助,麻烦动动发财的小手,点赞、关注和收藏,将是作者不断更新的动力🙏🏻*
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐