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;
}

真正的纯函数需要:

  1. 不偷看(无外部依赖)
  2. 不作弊(不修改外部状态)
  3. 不搞双标(相同输入永远同样输出)

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. 专家建议手册

  1. 引用类型处理:总是用展开运算符创建新对象
// 正确做法
const updateUser = user => ({ ...user, role: 'admin' });
  1. 副作用隔离:把IO操作放在管道最后
const processData = pipe(
  cleanData,
  transformFormat,
  data => saveToDB(data) // 副作用集中点
);
  1. 性能优化:用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. 结语:理想主义者的生存之道

经过这些案例的洗礼,你应该已经掌握:

✓ 纯函数就像瑞士钟表,精准可预测
✓ 高阶函数是代码复用的神器
✓ 组合函数让复杂流程变得像玩乐高

下次当你看到意大利面条式的代码时,不妨用这些技巧来化解。记住,好的代码不是写出来的,而是像泉水一样自然流淌出来的。