🧙♂️ 引言:今天召唤的不是神龙,是代码大神!
"你是不是觉得柯里化像《龙珠》里的黑悟空——神秘又高冷?🤯
其实它就像在玩集邮游戏!每拿到一个参数(邮票),就离召唤神龙(执行函数)更近一步!🐉
今天我们就用外卖订单、快递分拣员和俄罗斯套娃,把闭包和柯里化讲得比奶茶还好喝!🥤"
🧠 闭包:外卖订单的"记忆回放"功能
// 想象你在点外卖:选好套餐后,订单会记住你的名字!
function createOrder(name) {
return function(food) { // 🍕 这是外卖小哥的记忆卡
console.log(`${name} 订了 ${food}`);
}
}
const zhangsan = createOrder("张三"); // 🧾 订单已生成
zhangsan("麻辣香锅"); // 🧾 回忆订单并执行
⚠️ 小白注意点:闭包就像外卖订单,即使你离开柜台(函数执行完毕),订单信息(作用域)依然存在!
💡 闭包本质 = 函数 + 它能访问的自由变量(比如name)
🎯food是内部函数的参数,但name是外部函数的变量,这就是闭包的"跨代访问"魔法!
🛠 实际应用案例:租借工具的闭包
// 场景:租借工具公司,记录租借次数
function createToolRental(toolName) {
let rentals = 0;
return function() {
rentals++;
console.log(`${toolName} 已被租借 ${rentals} 次`);
};
}
const drillRental = createToolRental("电钻");
drillRental(); // 电钻 已被租借 1 次
drillRental(); // 电钻 已被租借 2 次
🧠 闭包在这里记录了租借次数(
rentals变量),即使函数被多次调用,状态依然保留!
🔐 闭包保护了rentals的私有性,外部无法直接修改计数器!
🔄 柯里化:快递分拣员的递归魔法📦
// 柯里化就像快递分拣:参数是快递,fn 是最终收件人
function curry(fn) {
let collectedArgs = [];
return function collector(...args) { // 📦 收集快递
collectedArgs = [...collectedArgs, ...args]; // 🧾 分拣快递
if (collectedArgs.length === fn.length) { // 🎉 龙珠集齐!
return fn(...collectedArgs); // 🐉 召唤神龙!
}
return collector; // 🔁 继续收集
}
}
🧠 递归逻辑图解:
collector函数就像俄罗斯套娃(🧩)——
每次调用都会返回一个新的"分拣员",直到参数集齐!
// ⚠️ 这里容易漏掉递归!(递归返回自己才是魔法核心)
🛠 实际应用案例:React 事件处理优化
// 场景:React 中的事件处理柯里化
function handleEvent(type, element) {
return function(event) {
console.log(`在 ${element} 上触发了 ${type} 事件`);
};
}
const clickHandler = handleEvent("点击", "按钮");
clickHandler(); // 在 按钮 上触发了 点击 事件
🧠 通过柯里化,将事件类型和元素固定,后续只需传递事件对象!
🚀 优势:避免重复传递相同参数,代码更简洁!
🧪 代码实验室:手写 curry 的三种姿势
1.js:柯里化基础版(龙珠收集器)🐉
function add(a, b, c) {
return a + b + c;
}
function curry(fn) {
let judge = (...args) => {
if (args.length == fn.length) { // 🎯 小白注意:fn.length 是函数定义时的参数数量
return fn(...args)
}
return (...newArgs) => judge(...args, ...newArgs) // 🔁 递归套娃
}
return judge
}
console.log(addCurry(1)(2)(3)); // 6 → 1+2+3=6(龙珠集齐!)
💡 柯里化本质 = 参数收集 + 递归判断 + 最终执行
🎯addCurry是 curry 返回的新函数,每次调用只收一个参数
2.js:类数组转真数组的魔法杖
function add(a, b, c) {
const args = Array.from(arguments); // 🧙♀️ 类数组变真数组的咒语
console.log(args.map(item => item + 1)); // ✅ 现在可以愉快地用数组方法啦!
let result = 0;
for (let i = 0; i < arguments.length; i++) {
result += arguments[i]; // 1+2+3=6
}
return result;
}
console.log(add.length); // 3(函数期望的参数数量)
console.log(add(1, 2, 3)); // 6
⚠️ 小白警告:
arguments是伪数组(没有 map 方法),要记得用Array.from()转换!
🎯arguments的长度由调用时实际传参决定,而fn.length是函数定义时的参数数
3.js:剩余参数的终极奥义(Spread Operator)✨
function add(...args) {
console.log(args); // [1,2,3](第一次调用)
return (...newArgs) => {
const arr = [...args, ...newArgs] // 🎯 小白注意:展开运算符合并数组
console.log(arr); // [1,2,3,4,5,6](第二次调用)
}
}
add(1,2,3)(4,5,6); // 先调用 add,再调用返回的函数
🧠 剩余参数
...args是 ES6 的终极武器,能一次性捕获所有参数!
🎯 与柯里化的递归收集不同,这是直接返回新函数的"两段式"玩法
🌈 总结:代码界的龙珠传说
闭包是外卖订单的记忆回放器,柯里化是快递分拣员的递归魔法!
每个参数都是龙珠,递归是套娃,最终执行是召唤神龙!🐉✨
记住:
- 闭包 = 函数 + 自由变量(外卖订单)
- 柯里化 = 参数收集 + 递归判断(龙珠收集)
- 剩余参数
...args是现代 JS 的瑞士军刀!
🧩 彩蛋互动:你的柯里化挑战!🚀
题目:用 curry 写一个乘法计算器!
要求:
multiply(2)(3)(4)应返回24- 提示:模仿 add 函数的柯里化逻辑
💡 答案彩蛋:
function multiply(a, b, c) { return a * b * c; } const multiplyCurry = curry(multiply); console.log(multiplyCurry(2)(3)(4)); // 24
🧠 柯里化的实际应用案例合集
1. 参数复用:正则校验器
// 场景:校验数字和字母
function curryingCheck(reg) {
return function(txt) {
return reg.test(txt);
};
}
const hasNumber = curryingCheck(/\d+/g);
const hasLetter = curryingCheck(/[a-z]+/g);
console.log(hasNumber('test1')); // true
console.log(hasLetter('21212')); // false
🧠 通过柯里化固定正则表达式,避免重复传递参数!
2. 延迟执行:API 请求封装
// 场景:封装 API 请求函数
function fetchData(url) {
return function(params) {
console.log(`Fetching data from ${url} with params:`, params);
};
}
const fetchFromAPI = fetchData('https://api.example.com');
fetchFromAPI({ q: 'search' }); // 输出:Fetching data from https://api.example.com with params: { q: 'search' }
🧠 柯里化延迟执行,先绑定 URL,再传递参数!
3. 函数组合:数据处理流水线
// 场景:组合函数计算最终价格
function calculatePrice(price, discount, taxRate) {
const discountedPrice = price * (1 - discount);
return discountedPrice * (1 + taxRate);
}
function curryCalculatePrice(price) {
return function(discount) {
return function(taxRate) {
const discountedPrice = price * (1 - discount);
return discountedPrice * (1 + taxRate);
};
};
}
const calculateWithDiscount = curryCalculatePrice(100)(0.1);
console.log(calculateWithDiscount(0.08)); // 97.2
🧠 柯里化拆分复杂函数,组合成灵活的数据处理链!
🌟 今日学习金句
"代码世界的魔法,不过是把复杂问题拆解成收集龙珠的小游戏!🐉"