JavaScript函数式编程核心概念与实战指南
本文系统介绍JavaScript中的函数式编程范式,涵盖一等公民函数、纯函数、不可变数据、递归及惰性求值等核心概念。通过对比命令式与声明式编程,解析map、filter、reduce等常用库函数的原理,并探讨JavaScript在函数式生态中的多范式特性与最佳实践。
编程范式
编程范式是一套用于思考问题及实现愿景的工具框架。现代编程语言大多采用多范式设计,支持面向对象、元程序设计、函数式以及面向过程等多种编程风格。

函数式编程范式
函数式编程如同氢燃料汽车,虽具前瞻性但尚未普及。与命令式编程通过语句更新全局状态不同,函数式编程将计算转化为表达式求值。这些表达式由纯数学函数构成,函数作为一等公民,可像普通值一样被处理且无副作用。

函数式编程高度重视以下原则:
函数是一等公民
应将函数视为与语言中其他对象同等的存在。这意味着可以将函数赋值给变量、动态创建函数,或将函数作为参数传递及返回值返回。

字符串可保存为变量,函数亦可,例如:
var sayHello = function() { return “Hello” };
字符串可存储为对象属性,函数亦可,例如:
var person = {message: “Hello”, sayHello: function() { return “Hello” }};
字符串可延迟创建,函数亦可,例如:
“Hello ” + (function() { return “World” })(); //=> Hello World
字符串可作为参数传入函数,函数亦可:
function hellloWorld(hello, world) { return hello + world() }
字符串可作为函数返回值,函数亦可,例如:
return “Hello”;
return function() { return “Hello”};
高阶函数

若函数接收其他函数作为参数或返回函数,则称为高阶函数。前文已展示示例,此处探讨更复杂的应用场景。
示例1:
[1, 2, 3].forEach(alert);
// alert 弹窗显示“1"
// alert 弹窗显示 "2"
// alert 弹窗显示 "3”
示例2:
function splat(fun) {
return function(array) {
return fun.apply(null, array);
};
}
var addArrayElements = splat(function(x, y) { return x + y });
addArrayElements([1, 2]);
//=> 3
推崇纯函数

纯函数不产生副作用,即不修改函数外部的状态。副作用包括:
-
修改变量
-
修改数据结构
-
设置外部变量属性
-
抛出异常或错误
最简单的例子是数学函数。Math.sqrt(4) 始终返回 2,不依赖状态或配置参数,且无任何副作用。
避免修改状态

函数式编程支持纯函数,这类函数不改变数据,常用于创建不可变数据。无需修改现有数据结构即可高效构建新数据。若纯函数通过修改局部数据生成不可变返回值,这是允许的。JavaScript 中少数默认不可变的数据类型包括 String:
var s = "HelloWorld";
s.toUpperCase();
//=> "HELLOWORLD"
s;
//=> "HelloWorld"
不可变状态的优势
• 避免混乱并提高程序准确性:复杂系统中,多数难以排查的 Bug 源于状态被外部代码意外修改。 • 确立“快速简洁”的多线程编程:若多线程可修改共享值,需同步获取,这既乏味又易错。软件事务内存和 Actor 模型提供了线程安全的修改处理方式。
使用递归替代循环

递归是函数式编程的标志性技术。递归函数即调用自身的函数。
递归是替代迭代循环的经典方式,每次执行函数体后处理集合下一项,直至满足结束条件。递归天然契合树形结构遍历等算法。
递归在任何语言中都是重要的函数式编程手段。许多函数式语言严格要求仅支持递归遍历,不支持显式循环,这要求语言消除尾调用优化,而 JavaScript 目前不支持此特性。
惰性求值优于急切计算
数学定义了许多无穷集合(如自然数),它们以符号表示。惰性求值(或非严格求值、按需调用)仅在需要时计算特定有限子集。急切求值会强制表示所有无穷数据,这显然不可行。
许多语言默认支持惰性求值,并提供惰性数据结构来表达无穷集合,按需精确计算。
代码 result = compute() 表示将 compute() 的返回值赋给 result,但 result 的值仅在真正使用时才有意义。
可见策略的选择能显著提升性能,尤其在链式处理或数组处理中。这是函数式程序员偏爱的技术。
这开启了并发执行、并行技术及组合等多种可能性。
然而,JavaScript 自身不支持惰性求值。尽管如此,JavaScript 函数库可有效模拟惰性求值行为。
闭包的全部优势
所有函数式语言都具备闭包,尽管这一特性常被视为神秘。闭包是包含对内部引用变量隐式绑定的函数,即函数封闭了其引用的上下文。JavaScript 中的闭包允许函数访问父级作用域,即使父函数已执行完毕。
function multiplier(factor) {
return function(number) {
return number * factor;
};
}
var twiceOf = multiplier(2);
console.log(twiceOf(6));
//=> 12
声明式优于命令式编程
函数式编程是声明式的,如同数学运算,定义属性与关系,运行时计算最终结果。阶乘函数定义如下:
factorial(n) = 1 if n = 1
n * factorial(n-1) if n > 1
该定义将 factorial(n) 关联到 factorial(n-1),属于递归定义。特殊情况 factorial(1) 终止递归。
var imperativeFactorial = function(n) {
if(n == 1) {
return 1
} else {
product = 1;
for(i = 1; i <= n; i++) {
product *= i;
}
return product;
}
}
var declarativeFactorial = function(n) {
if(n == 1) {
return 1
} else {
return n * factorial(n - 1);
}
}
尽管实现阶乘计算看似“命令式”,但其结构更接近声明式。
命令式阶乘使用可变值、循环计数器和结果累加,显式实现特定算法。与声明式版本相比,该方法包含多个可变步骤,更难理解且易引入 Bug。

函数式 JavaScript 库
存在众多函数式库:underscore.js、lodash、Fantasy Land、Functional.js、Bilby.js、fn.js、Wu.js、Lazy.js、Bacon.js、sloth.js、stream.js、Sugar、Folktale、RxJs 等。
函数式程序员工具包
map()、filter() 和 reduce() 构成函数式程序员工具包的核心。纯高阶函数是函数式方法的主力,它们以函数为输入,返回无副作用的输出。
这些 JavaScript 函数对每位函数式程序员至关重要。它们消除循环和语句,使代码更整洁。作为 ECMAScript 5.1 标准的一部分,它们仅处理数组,每次调用创建并返回新数组,不修改原数组。此外,它们接受函数(通常为匿名回调)作为参数,遍历数组并将回调应用于每一项。
myArray = [1,2,3,4];
newArray = myArray.map(function(x) {return x*2});
console.log(myArray); // Output: [1,2,3,4]
console.log(newArray); // Output: [2,4,6,8]
除上述三个函数外,还有许多可插入几乎每个函数式应用的函数:
forEach()、concat()、reverse()、sort()、every() 以及 some()。
JavaScript 的多范式特性
JavaScript 并非严格的函数式编程语言,这促使了其他范式的使用:
-
命令式编程:基于详细操作描述的编程
-
基于原型的面向对象编程:基于原型对象及其实例的编程
-
元程序编程:操纵 JavaScript 执行模型的编程方式。元程序编程的定义为“编程发生在您书写代码做某事的时候,而元程序编程则发生在您书写代码导致某事的解释方式发生变化的时候。”
原文地址:https://dzone.com/articles/functional-programming-using-javascript-part-1
翻译:simei231, 祝青, 李中凯
JavaScript异步发送HTTP请求时怎么优雅地处理错误
2026-11-05 17:10:44
5款主流JS在线运行工具对比:CodePen、JSFiddle与StackBlitz怎么选
2026-11-05 17:04:04
JavaScript入门书籍避坑:这些书不适合初学者
2026-11-05 16:31:15
JS使用Math.round实现保留两位小数的原理与代码
2026-11-05 15:35:55
JavaScript 原生 Date 对象怎么实现日期加减
2026-11-05 14:30:00

