13.1 纯函数与副作用
13.1 纯函数与副作用
函数式编程(Functional Programming)是 JavaScript 中的重要范式,而纯函数(Pure Function)和副作用(Side Effect)是理解函数式编程的核心概念。本节将详细介绍纯函数的定义、特点以及副作用的影响,并通过示例代码展示如何编写纯函数和避免副作用。
13.1.1 纯函数(Pure Function)
1. 定义
纯函数是指满足以下两个条件的函数:
- 相同的输入始终返回相同的输出(确定性)。
- 不依赖或改变外部状态(无副作用)。
2. 示例
// 纯函数
function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // 总是输出 5
3. 特点
- 可缓存性:由于输入输出恒定,可以缓存结果。
- 可测试性:不依赖外部状态,易于单元测试。
- 并行安全:无共享状态,适合并行执行。
13.1.2 副作用(Side Effect)
1. 定义
副作用是指函数在执行过程中对外部状态产生的影响,例如:
- 修改全局变量。
- 修改输入参数。
- 发起网络请求。
- 操作 DOM。
2. 示例
// 有副作用的函数
let count = 0;
function increment() {
count++; // 修改外部变量
return count;
}
console.log(increment()); // 输出 1(结果依赖外部状态)
13.1.3 纯函数 vs 非纯函数
| 特性 | 纯函数 | 非纯函数 |
|---|---|---|
| 输入输出确定性 | ✅ 相同输入总是相同输出 | ❌ 输出可能依赖外部状态 |
| 副作用 | ❌ 无 | ✅ 可能修改外部状态或依赖外部状态 |
| 可测试性 | ✅ 高 | ❌ 低(需模拟外部环境) |
| 可缓存性 | ✅ 可缓存 | ❌ 不可缓存 |
13.1.4 如何避免副作用?
1. 不修改输入参数
// 非纯函数(修改输入参数)
function updateUser(user) {
user.age = 25; // 直接修改参数
return user;
}
// 纯函数(返回新对象)
function updateUserPure(user) {
return { ...user, age: 25 }; // 使用展开运算符创建新对象
}
2. 隔离副作用
将副作用限制在可控范围内(如函数组合的最外层):
// 将副作用隔离到单独的函数中
function fetchData(url) {
return fetch(url); // 副作用(网络请求)在此发生
}
// 纯函数处理数据
function processData(data) {
return data.filter(item => item.isActive);
}
3. 使用不可变数据
通过库(如 Immutable.js)或原生方法(如 Object.freeze)避免直接修改数据:
const user = Object.freeze({ name: "Alice", age: 24 });
function updateAge(user, newAge) {
return { ...user, age: newAge }; // 返回新对象
}
13.1.5 示例代码
示例 1:纯函数
// 纯函数:计算数组平均值
function calculateAverage(numbers) {
const sum = numbers.reduce((acc, num) => acc + num, 0);
return sum / numbers.length;
}
console.log(calculateAverage([1, 2, 3])); // 输出 2
示例 2:隔离副作用
// 非纯部分(副作用)
function logMessage(message) {
console.log(message); // 副作用(I/O 操作)
}
// 纯函数部分
function formatMessage(user, text) {
return `${user.name}: ${text}`;
}
const user = { name: "Alice" };
const message = formatMessage(user, "Hello");
logMessage(message); // 输出 "Alice: Hello"
示例 3:避免修改输入
// 非纯函数(修改输入)
function addToCartImpure(cart, item) {
cart.push(item); // 直接修改输入参数
return cart;
}
// 纯函数(返回新数组)
function addToCartPure(cart, item) {
return [...cart, item]; // 使用展开运算符创建新数组
}
13.1.6 总结
- 纯函数:输入输出确定、无副作用,适合核心逻辑。
- 副作用:需谨慎控制,尽量隔离到代码外层。
- 实践建议:
- 优先编写纯函数。
- 将副作用(如 API 调用、DOM 操作)集中管理。
- 使用不可变数据避免意外修改。
通过理解纯函数和副作用,你可以编写更可预测、更易维护的代码。在接下来的学习中,我们将探索函数式编程的其他高级特性。
思考题:
- 为什么纯函数更适合单元测试?
- 如何将一个依赖全局变量的非纯函数改造成纯函数?
- 在哪些场景下副作用是不可避免的?如何管理它们?
#前端开发
分享于 2025-03-24