JavaScript函数式编程核心概念:纯函数、副作用与不可变性
本文详解JavaScript中的函数式编程范式,通过纯函数、副作用控制及不可变性三大核心概念,展示如何将命令式代码重构为高复用、低耦合的结构。内容涵盖高阶函数拆分、依赖注入及深拷贝技巧,旨在提升代码稳定性与可测试性。
函数式编程
函数式编程是一种构建软件的结构风格,它将计算过程视为对数学函数的求值,核心在于避免状态变更和数据可变。这与命令式编程形成鲜明对比。例如,若需实现数组元素加一的需求:
// 数组每个数字加一, 命令式编程
let arr = [1, 2, 3, 4];
let newArr = [];
for(let i = 0; i < arr.length; i++){
newArr.push(arr[i] + 1);
}
console.log(newArr); // [2, 3, 4, 5]
上述命令式代码虽能正确运行,但缺乏复用性。我们可以转换思路,将操作拆解为两个独立部分:遍历数组和执行具体逻辑。将这两者分离后:
// 先拆加一出来
let add1 = x => x +1;
// 然后拆遍历方法出来,通过遍历返回一个操作后的新数组
// fn是我们需要对每个数组想进行的操作
let createArr = (arr, fn) => {
const newArr = [];
for(let i = 0; i < arr.length; i++){
newArr.push(fn(arr[i]));
}
return newArr;
}
// 用这两个方法来得到我们期望的结果
const arr = [1, 2, 3, 4];
const newArr = createArr(arr, add1);
console.log(newArr); // [2, 3, 4, 5], 结果仍然是对的
拆分后,若后续需求变为将数组元素乘以2,只需新增乘法逻辑并复用遍历代码即可:
let multiply2 = x => x * 2;
// 调用之前的createArr
const arr2 = [1, 2, 3, 4];
const newArr2 = createArr(arr2, multiply2);
console.log(newArr2); // [2, 4, 6, 8], 结果是对的
然而,上述加一函数仍不够通用,复用性有限。我们可以进一步将其拆解:
// 先写一个通用加法,他接收第一个加数,返回一个方法
// 返回的这个方法接收第二个加数,第一个加数是上层方法的a
// 这样当我们需要计算1+2是,就是add(1)(2)
let add = (a) => {
return (b) => {
return a + b;
}
}
// 我们也可以将返回的函数赋给一个变量,这个变量也就变成一个能特定加a的一个方法
let add1 = add(1);
let res = add1(4);
console.log(res); // 5
由此可见,函数式编程的本质是将程序分解为更高复用、更可靠且更易理解的模块,再通过组合这些模块构建出逻辑清晰的程序整体。
纯函数
纯函数指在相同输入下始终返回相同结果的函数。它不依赖外部状态或数据变化,仅由输入参数决定输出,且不会修改外部数据,仅通过返回值与外界交互。以下函数因依赖外部变量discount,故不属于纯函数:
let discount = 0.8;
const calPrice = price => price * discount;
let price = calPrice(200); // 160
// 当discount变了,calPrice传同样额参数,结果不一样,所以不纯
discount = 0.9;
price = calPrice(200); // 180
将其改造为纯函数十分简单,只需将discount作为参数传入:
const calPrice = (price, discount) => price * discount;
纯函数能确保代码稳定性,因为相同输入必然对应相同输出。反之,不纯的函数可能引入副作用。
函数副作用
函数副作用指除返回值外,对主调用方产生额外影响的行为,如修改全局变量、外部变量或参数。这会导致难以追踪的Bug并降低代码可读性。例如,以下foo函数存在副作用,若后续代码依赖变量a,可能获取到被污染的值:
let a = 5;
let foo = () => a = a * 10;
foo();
console.log(a); // 50
除了自定义函数,部分原生API也可能产生副作用,编码时需特别注意:

我们的目标是尽可能减少副作用,将函数编写为纯函数。以下函数因依赖new Date(),每次执行结果不同,故不纯:

要将其改为纯函数,可采用依赖注入方式,将不纯的部分提取为参数。这样可将副作用代码集中处理于外部,远离核心逻辑,从而保障核心代码的稳定性:
// 依赖注入
const foo = (d, log, something) => {
const dt = d.toISOString();
return log(`${dt}: ${something}`);
}
const something = 'log content';
const d = new Date();
const log = console.log.bind(console);
foo(d, log, something);
因此,减少副作用的通用方法包括:
1. 函数仅通过参数进行运算,不修改参数本身 2. 函数内部不修改外部变量 3. 运算结果通过返回值返回给外部
可变性和不可变性
- 可变性:指变量创建后允许被任意修改。
- 不可变性:指变量创建后其值永远不会发生改变。不可变性是函数式编程的核心基石。
以下是一个可变操作的示例:

若必须修改参数,应先对该参数进行深拷贝,再对副本进行操作,从而避免修改原始参数:

至此,关于JavaScript函数式编程的详解已介绍完毕。如需了解更多JS函数式编程内容,请查阅本站往期文章或继续浏览后续相关教程,感谢大家的支持!
JavaScript闭包与类(Class)的区别及适用场景对比
2026-11-05 15:48:57
JavaScript入门要学ES6吗?let与箭头函数等新特性解析
2026-11-05 14:49:16
JavaScript 8种数据类型详解:基本类型与引用类型的区别
2026-11-05 14:25:15
怎么在JavaScript中使用async/await优化Ajax异步流程
2026-11-05 13:56:15
JavaScript replace回调函数实现动态正则替换逻辑
2026-11-05 13:52:13
