函数式编程概念

理解纯函数、柯里化、高阶函数和副作用

问题

解释函数式编程中的纯函数、柯里化、高阶函数和副作用。

解答

纯函数

相同输入永远返回相同输出,且没有副作用。

// 纯函数:只依赖输入,不修改外部状态
function add(a, b) {
  return a + b;
}

// 非纯函数:依赖外部变量
let count = 0;
function increment() {
  return ++count; // 每次调用结果不同
}

// 非纯函数:修改了输入参数
function addItem(arr, item) {
  arr.push(item); // 修改了原数组
  return arr;
}

// 纯函数版本
function addItemPure(arr, item) {
  return [...arr, item]; // 返回新数组
}

副作用

函数执行过程中对外部环境产生的影响。

// 常见副作用
function sideEffects() {
  console.log('打印日志');        // 控制台输出
  document.title = 'New Title';  // 修改 DOM
  localStorage.setItem('key', 'value'); // 存储操作
  fetch('/api');                 // 网络请求
}

// 管理副作用:将副作用隔离
function calculatePrice(price, discount) {
  return price * (1 - discount); // 纯计算
}

function displayPrice(price) {
  document.getElementById('price').textContent = price; // 副作用隔离
}

高阶函数

接收函数作为参数或返回函数的函数。

// 接收函数作为参数
const numbers = [1, 2, 3, 4, 5];

const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
const sum = numbers.reduce((acc, n) => acc + n, 0);

// 返回函数
function multiply(a) {
  return function(b) {
    return a * b;
  };
}

const double = multiply(2);
console.log(double(5)); // 10

// 实际应用:防抖函数
function debounce(fn, delay) {
  let timer = null;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

const debouncedSearch = debounce(query => {
  console.log('搜索:', query);
}, 300);

柯里化

将多参数函数转换为一系列单参数函数。

// 普通函数
function add(a, b, c) {
  return a + b + c;
}
add(1, 2, 3); // 6

// 柯里化版本
function curriedAdd(a) {
  return function(b) {
    return function(c) {
      return a + b + c;
    };
  };
}
curriedAdd(1)(2)(3); // 6

// 箭头函数简写
const curriedAddArrow = a => b => c => a + b + c;

// 通用柯里化函数
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));
    };
  };
}

// 使用
const curriedSum = curry((a, b, c) => a + b + c);
console.log(curriedSum(1)(2)(3));   // 6
console.log(curriedSum(1, 2)(3));   // 6
console.log(curriedSum(1)(2, 3));   // 6

// 柯里化应用:参数复用
const log = curry((level, message) => {
  console.log(`[${level}] ${message}`);
});

const info = log('INFO');
const error = log('ERROR');

info('应用启动');   // [INFO] 应用启动
error('连接失败');  // [ERROR] 连接失败

函数组合

将多个函数组合成一个函数。

// compose:从右到左执行
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);

// pipe:从左到右执行
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);

// 示例
const addOne = x => x + 1;
const double = x => x * 2;
const square = x => x * x;

const compute = pipe(addOne, double, square);
console.log(compute(2)); // ((2 + 1) * 2)² = 36

// 实际应用:数据处理管道
const users = [
  { name: 'Alice', age: 25, active: true },
  { name: 'Bob', age: 30, active: false },
  { name: 'Charlie', age: 35, active: true }
];

const getActiveNames = pipe(
  users => users.filter(u => u.active),
  users => users.map(u => u.name),
  names => names.join(', ')
);

console.log(getActiveNames(users)); // "Alice, Charlie"

关键点

  • 纯函数:相同输入相同输出,无副作用,便于测试和缓存
  • 副作用:对外部环境的影响,应隔离管理而非完全消除
  • 高阶函数:函数作为参数或返回值,是函数式编程的基础
  • 柯里化:参数复用、延迟执行、函数组合的基础
  • 函数组合:将小函数组合成复杂功能,提高代码复用性