1. 写给卷不动又躺不平的你
在这个连"Hello World"都要用AI生成的时代,很多前端开发者都面临着相同的焦虑:为什么我的代码总是像乐高积木拼出来的变形金刚?当项目进入维护阶段,我们都在凌晨3点的台灯下问过自己:这坨绕来绕去的逻辑能不能优雅点?
本文不会教你修仙式的编程秘诀,但会通过咖啡般苦中带甜的案例,让你掌握三个核心武器:纯函数让代码可预测,高阶函数让逻辑可复用,组合函数让流程更清晰。这些概念听起来很玄学?别急,我们马上用最接地气的方式拆解。
2. 纯函数:代码世界的理想主义者
2.1 什么是纯函数界的"三不原则"
想象你暗恋的同事帮你冲咖啡:
// 非纯函数版 - 咖啡可能变成奶茶
function makeCoffee(beans, water) {
return window.mysteryPowder + beans + water; // 依赖外部变量
}
// 纯函数版 - 严格遵守规则
function pureCoffee(beans = 15, water = 200) {
const standardCup = beans * 5 + water;
localStorage.setItem('lastCoffee', standardCup); // 产生副作用 ❌
return standardCup;
}
真正的纯函数需要:
- 不偷看(无外部依赖)
- 不作弊(不修改外部状态)
- 不搞双标(相同输入永远同样输出)
2.2 真实项目的改造案例
看这个电商购物车计算器:
// 改造前:购物车刺客
let discount = 0.8; // 外部变量
function calculateTotal(items) {
return items.reduce((sum, item) => {
item.price = item.basePrice * discount; // 修改入参 ❌
return sum + item.price;
}, 0);
}
// 改造后:纯函数战士
function pureCalculate(items, currentDiscount) {
return items.reduce((sum, { basePrice }) => {
return sum + basePrice * currentDiscount;
}, 0);
}
改造后的函数就像乐高积木,可以任意组合不产生副作用。
3. 高阶函数:代码魔术师的帽子
3.1 日常开发中的神兵利器
高阶函数如同瑞士军刀,看这个请求重试机制:
// 基础版请求函数
async function fetchData(url) {
const response = await fetch(url);
return response.json();
}
// 高阶函数工厂
function createRetry(fn, retries = 3) {
return async (...args) => {
for (let i = 0; i < retries; i++) {
try {
return await fn(...args);
} catch (error) {
if (i === retries - 1) throw error;
await new Promise(res => setTimeout(res, 1000 * (i + 1)));
}
}
};
}
// 生成带重试的fetch
const robustFetch = createRetry(fetchData, 5);
这个模式可以用来创建:
- 自动缓存版本
- 日志记录版本
- 权限校验版本
3.2 你每天都在用的高阶函数
别再让同事嘲笑你用不好数组方法了:
// 常见误区纠正
const products = [{ price: 100 }, { price: 200 }];
// 错误姿势:
const total = products
.map(p => p.price * 1.17) // 增值税
.filter(price => price > 100)
.reduce((sum, price) => sum + price, 0);
// 专家级写法:
const addTax = rate => p => p.price * (1 + rate);
const filterPrice = min => price => price > min;
const expertTotal = products
.map(addTax(0.17))
.filter(filterPrice(100))
.reduce((sum, price) => sum + price, 0);
4. 组合函数:代码乐高大师
4.1 组合的艺术
看这个用户信息处理流程:
// 基础函数库
const fetchUser = async id => ({ id, name: `User${id}`, role: 'guest' });
const checkPermission = user => ({ ...user, access: user.role === 'admin' });
const formatUser = user => ({
...user,
displayName: user.access ? `★ ${user.name}` : user.name
});
// 组合魔法
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);
const getUserProfile = pipe(
fetchUser,
user => checkPermission(user),
formatUser
);
// 使用
getUserProfile(123).then(console.log);
这样的组合就像代码的乐高积木,随时可以替换其中任意模块。
4.2 复杂表单验证实战
// 验证规则库
const minLength = len => value => value.length >= len;
const maxLength = len => value => value.length <= len;
const hasNumber = value => /\d/.test(value);
// 组合验证器
const createValidator = (...rules) => value =>
rules.every(rule => rule(value));
// 密码验证组合
const passwordValidator = createValidator(
minLength(8),
maxLength(20),
hasNumber
);
// 使用示例
console.log(passwordValidator('weak')); // false
console.log(passwordValidator('Str0ngP@ss')); // true
5. 三大剑客的联合作战
电商平台真实案例
让我们构建一个促销计算系统:
// 基础函数
const getBasePrice = cart => cart.items.reduce((sum, i) => sum + i.price, 0);
const applyDiscount = rate => price => price * (1 - rate);
const addShipping = threshold => price => price > threshold ? price : price + 15;
// 复合计算流程
const calculateTotal = pipe(
getBasePrice,
price => applyDiscount(0.2)(price), // 8折
addShipping(100), // 满100免运费
Math.round
);
// 测试数据
const cart = {
items: [
{ price: 80 },
{ price: 50 }
]
};
console.log(calculateTotal(cart)); // (130*0.8)=104 → 104(免运费)
6. 生存指南:何时该用/不该用
适用场景:
- 数据处理流水线(日志分析、报表生成)
- 可复用的业务规则(表单验证、权限控制)
- 状态管理中间件
- 单元测试友好型代码
可能掉坑场景:
- 过度抽象导致可读性下降
- 性能敏感场景(大数据量处理)
- 需要显式副作用的场景(日志记录、API调用)
- 团队技能不统一时的协作难题
7. 专家建议手册
- 引用类型处理:总是用展开运算符创建新对象
// 正确做法
const updateUser = user => ({ ...user, role: 'admin' });
- 副作用隔离:把IO操作放在管道最后
const processData = pipe(
cleanData,
transformFormat,
data => saveToDB(data) // 副作用集中点
);
- 性能优化:用memoization缓存昂贵计算
const memoize = fn => {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
return cache.has(key) ? cache.get(key) :
cache.set(key, fn(...args)) && cache.get(key);
};
};
8. 结语:理想主义者的生存之道
经过这些案例的洗礼,你应该已经掌握:
✓ 纯函数就像瑞士钟表,精准可预测
✓ 高阶函数是代码复用的神器
✓ 组合函数让复杂流程变得像玩乐高
下次当你看到意大利面条式的代码时,不妨用这些技巧来化解。记住,好的代码不是写出来的,而是像泉水一样自然流淌出来的。