JavaScript 高阶函数与柯里化完整指南

192 阅读3分钟

JavaScript 高阶函数与柯里化完整指南

1. 高阶函数基础

1.1 定义

高阶函数是指满足下列条件之一的函数:

  • 接受一个或多个函数作为参数
  • 返回一个函数作为结果

1.2 基本示例

// 接受函数作为参数
function operate(x, y, operator) {
  return operator(x, y);
}

// 使用示例
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;

console.log(operate(5, 3, add));      // 8
console.log(operate(5, 3, multiply)); // 15

// 返回函数作为结果
function createMultiplier(factor) {
  return function(number) {
    return number * factor;
  };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(5));  // 10
console.log(triple(5));  // 15

2. 常见的高阶函数

2.1 Array 方法中的高阶函数

const numbers = [1, 2, 3, 4, 5];

// map
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

// filter
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4]

// reduce
const sum = numbers.reduce((acc, curr) => acc + curr, 0);
console.log(sum); // 15

// forEach
numbers.forEach(num => console.log(num));

2.2 自定义高阶函数

// 函数组合
function compose(...fns) {
  return function(x) {
    return fns.reduceRight((value, fn) => fn(value), x);
  };
}

const addOne = x => x + 1;
const double = x => x * 2;
const addThenDouble = compose(double, addOne);

console.log(addThenDouble(3)); // 8 (3 + 1) * 2

// 函数记忆(缓存)
function memoize(fn) {
  const cache = new Map();
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) {
      return cache.get(key);
    }
    const result = fn.apply(this, args);
    cache.set(key, result);
    return result;
  };
}

const expensiveFunction = memoize((n) => {
  console.log('Computing...');
  return n * 2;
});

console.log(expensiveFunction(5)); // Computing... 10
console.log(expensiveFunction(5)); // 10 (从缓存中获取)

3. 函数柯里化

3.1 柯里化定义

柯里化是将一个多参数函数转换成一系列单参数函数的过程。

3.2 基本实现

// 简单的柯里化实现
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    }
    return function(...moreArgs) {
      return curried.apply(this, args.concat(moreArgs));
    };
  };
}

// 使用示例
function add(a, b, c) {
  return a + b + c;
}

const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3));     // 6
console.log(curriedAdd(1, 2)(3));     // 6
console.log(curriedAdd(1)(2, 3));     // 6

3.3 高级柯里化实现

// TypeScript 版本的柯里化
type Curry<P extends any[], R> =
  P extends [infer First, ...infer Rest]
    ? (arg: First) => Rest extends []
      ? R
      : Curry<Rest, R>
    : R;

function curryTyped<P extends any[], R>(
  fn: (...args: P) => R
): Curry<P, R> {
  return function curried(...args: any[]): any {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    }
    return function(...moreArgs: any[]) {
      return curried.apply(this, args.concat(moreArgs));
    };
  } as any;
}

// 使用示例
interface User {
  name: string;
  age: number;
}

function updateUser(id: number, key: keyof User, value: User[keyof User]) {
  console.log(`Updating user ${id}: ${key} = ${value}`);
}

const curriedUpdate = curryTyped(updateUser);
const updateUser1 = curriedUpdate(1);
const updateUser1Name = updateUser1('name');
updateUser1Name('John'); // Updating user 1: name = John

4. 实际应用场景

4.1 事件处理

// 事件处理器生成器
const createEventHandler = (eventType) => (handler) => (element) => {
  element.addEventListener(eventType, handler);
  return element;
};

const handleClick = createEventHandler('click');
const handleHover = createEventHandler('mouseover');

const button = document.querySelector('button');
handleClick(e => console.log('Clicked'))(button);
handleHover(e => console.log('Hovered'))(button);

4.2 数据转换

// 数据转换管道
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);

const capitalize = str => str.toUpperCase();
const addExclamation = str => `${str}!`;
const repeat = str => `${str} ${str}`;

const transform = pipe(
  capitalize,
  addExclamation,
  repeat
);

console.log(transform('hello')); // "HELLO! HELLO!"

4.3 API 调用

// API 调用包装器
const createApiCall = (baseUrl) => (endpoint) => async (params) => {
  try {
    const response = await fetch(`${baseUrl}${endpoint}`, {
      method: 'POST',
      body: JSON.stringify(params)
    });
    return response.json();
  } catch (error) {
    console.error('API Error:', error);
    throw error;
  }
};

const api = createApiCall('https://api.example.com');
const getUserData = api('/users');
const getOrderData = api('/orders');

// 使用
getUserData({ id: 123 }).then(data => console.log(data));

5. 最佳实践

5.1 性能考虑

  1. 使用记忆化避免重复计算
  2. 注意闭包导致的内存占用
  3. 合理使用柯里化,避免过度抽象

5.2 代码组织

  1. 保持函数纯净
  2. 使用有意义的函数名
  3. 适当的错误处理
  4. 注意函数组合的顺序

5.3 调试技巧

  1. 使用 console.log 跟踪函数执行
  2. 在开发环境保留详细的错误信息
  3. 使用 TypeScript 增加类型安全

6. 总结

高阶函数和柯里化的优点:

  1. 提高代码复用性
  2. 增加代码灵活性
  3. 提供更好的抽象
  4. 支持函数式编程范式

使用建议:

  1. 根据实际需求选择合适的抽象级别
  2. 平衡代码可读性和灵活性
  3. 考虑团队成员的接受程度
  4. 注意性能影响 高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。 1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。 2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。常见的高阶函数有:Promise、setTimeout、arr.map()等等 函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。