JavaScript 函数式编程入门
函数式编程(FP)是一种将计算视为数学函数应用的编程范式。与编写一系列改变程序状态的指令的命令式方法不同,函数式风格关注的是需要计算什么,而不是如何计算。
JavaScript 作为一种多范式语言,非常适合函数式编程。在 Node.js 环境中,这种方法对于数据处理、流操作以及构建可靠的服务器端应用尤其有用。本文将通过实际示例解析函数式编程的核心概念。
函数式编程的核心原则
纯函数
纯函数是指:
- 相同的参数始终返回相同的结果(确定性)。
- 没有副作用(不修改外部变量,不向控制台输出,不写入文件)。
// Pure functionconst add = (a, b) => a + b;
// Impure function (side effect — console output)const impureAdd = (a, b) => { console.log(`Adding ${a} and ${b}`); return a + b;};
// Impure function (depends on external state)let counter = 0;const increment = () => ++counter;纯函数的优点:易于测试、调试和并行化。它们是可预测的,且不依赖于执行上下文。
不可变性
不可变性意味着数据一旦创建就不能被改变。我们不是修改现有对象,而是创建一份带有所需更改的新副本。
// Mutable approach (bad)const user = { name: 'Ivan', age: 30 };user.age = 31; // Directly modifying
// Immutable approach (good)const user2 = { name: 'Maria', age: 25 };const updatedUser = { ...user2, age: 26 };// The original user2 object remains unchanged
// Working with arraysconst numbers = [1, 2, 3];const newNumbers = [...numbers, 4]; // [1, 2, 3, 4]// numbers remains [1, 2, 3]在 Node.js 中,不可变性在处理请求和应用状态时尤其重要,可以避免异步代码中出现意外的修改。
高阶函数与闭包
高阶函数
这些函数接受其他函数作为参数或返回函数。内置数组方法 map、filter 和 reduce 是经典例子。
const numbers = [1, 2, 3, 4, 5];
// map — transforms each elementconst doubled = numbers.map(n => n * 2);// [2, 4, 6, 8, 10]
// filter — keeps elements based on a conditionconst evens = numbers.filter(n => n % 2 === 0);// [2, 4]
// reduce — reduces the array to a single valueconst sum = numbers.reduce((acc, n) => acc + n, 0);// 15闭包
闭包是一个函数,它"记住"了创建时所在作用域中的变量,即使该作用域已执行完毕。
const createCounter = () => { let count = 0; // Private variable return () => ++count;};
const counter1 = createCounter();console.log(counter1()); // 1console.log(counter1()); // 2
const counter2 = createCounter();console.log(counter2()); // 1 (independent counter)闭包被广泛用于创建函数工厂以及以函数式风格封装状态。
柯里化与函数组合
柯里化
柯里化是将一个接受多个参数的函数转换为一系列函数的过程,每个函数接受一个参数。