JavaScript中的函数式编程:从理论到实践
函数式编程(FP)是一种将计算视为数学函数求值,并避免改变状态和可变数据的编程范式。在现代JavaScript中,特别是在Node.js环境下,由于其可预测性、可测试性和模块化程度高,函数式方法正变得越来越流行。在本文中,我们将分解FP的关键概念,并展示如何在实际项目中应用它们。
1. 纯函数与无副作用
纯函数是指:
- 相同的参数总是返回相同的结果(确定性)。
- 不产生副作用(不修改外部变量,不输出数据,不访问文件系统)。
纯函数示例:
// 纯函数:两数相加const add = (a, b) => a + b;
console.log(add(2, 3)); // 5console.log(add(2, 3)); // 5 — 始终相同非纯函数示例:
let counter = 0;const increment = () => { counter++; // 副作用:修改了外部变量 return counter;};
console.log(increment()); // 1console.log(increment()); // 2 — 结果不同在函数式编程中,我们力求只编写纯函数。这使得代码更易于测试和调试。
2. 数据不可变性
不可变性意味着数据在创建后不会被改变。我们不是修改现有对象,而是创建一个带有必要更改的新副本。在JavaScript中,经常使用扩展运算符(...)或Object.assign方法来实现这一点。
不可变对象更新示例:
const user = { name: 'Alice', age: 30 };
// 不好的做法:改变了原对象user.age = 31;
// 好的做法:创建一个新对象const updatedUser = { ...user, age: 31 };
console.log(user); // { name: 'Alice', age: 30 }console.log(updatedUser); // { name: 'Alice', age: 31 }对于数组,我们也使用不可变方法:
const numbers = [1, 2, 3];
// 变异操作(不好的做法)numbers.push(4);
// 不可变操作(好的做法)const newNumbers = [...numbers, 4];
console.log(numbers); // [1, 2, 3]console.log(newNumbers); // [1, 2, 3, 4]3. 高阶函数:map, filter, reduce
高阶函数是将其他函数作为参数或返回函数的函数。JavaScript中最流行的三个方法是map、filter和reduce。它们允许无需循环和变异操作来处理数组。
3.1. map() — 转换数组
map方法通过将函数应用于原始数组的每个元素来创建一个新数组。
const prices = [100, 200, 300];const withTax = prices.map(price => price * 1.2);
console.log(withTax); // [120, 240, 360]// 原始数组未改变console.log(prices); // [100, 200, 300]3.2. filter() — 过滤数组
filter方法返回一个新数组,其中仅包含传入函数返回true的元素。
const ages = [15, 22, 18, 30, 12];const adults = ages.filter(age => age >= 18);
console.log(adults); // [22, 18, 30]3.3. reduce() — 数据聚合
reduce方法将数组归约为单个值(数字、对象、字符串)。
const numbers = [1, 2, 3, 4, 5];const sum = numbers.reduce((accumulator, current) => accumulator + current, 0);
console.log(sum); // 15通过组合这些方法,你可以以函数式风格解决复杂问题:
const users = [