什么是函数式编程
如果让你写一个这个需求你会怎么写?
写一个计算数组中所有偶数平方和的例子
是这样?
js
function imperativeSumOfSquaredEvens(arr) {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
if (arr[i] % 2 === 0) {
sum += arr[i] * arr[i];
}
}
return sum;
}
还是这样?
js
function functionalSumOfSquaredEvens(arr) {
return arr
.filter(num => num % 2 === 0)
.map(num => num * num)
.reduce((acc, num) => acc + num, 0);
}
命令式编程是一种基于存储程序概念的编程范式,通过修改可变状态来改变程序行为,核心是关注how
(如何做)即详细描述操作步骤,使用可变状态,允许副作用,使用循环、复制和控制流语句。
函数式编程是一种基于函数的强大的编程范式,将计算机运算视为函数运算,避免使用程序状态以及易变对象。核心是关注what
(需要什么)即函数组合,强调不可变性,使用纯函数没有副作用,广泛使用高阶函数。
在实际开发中,两者常常结合使用,根据具体场景选择最适合的编程方式
核心基础概念
纯函数(Pure Function)
定义:相同输入始终产生相同输出,且没有副作用的函数。可预测、便于测试、调试和并行处理(无状态,不会发生竞态条件)
js
// 纯函数示例
function add(a, b){
return a + b
}
// 非纯函数示例
let counter = 0
function increment() {
counter++
return counter
}
不可变数据(Immutability)
定义:数据一旦创建,就不能被修改,任何"修改"操作都会创建新数据。避免意外修改数据,简化状态管理,便于实现函数式编程的其他特性
js
// 不可变操作示例
const originalArray = [1, 2, 3]
const newArray = [...originalArray, 4]
// 可变操作(不推荐)
const mutableArray = [1, 2, 3]
mutableArray.push(4)
函数作为一等公民(First-class Functions)
定义:函数可以像普通变量一样被传递、存储和操作
js
// 函数作为变量
const add = (a, b) => a + b
// 函数作为参数
const process = (array, func) => array.map(func)
const result = process([1, 2, 3], add)
// 函数作为返回值
const createMultiplier = factor => number => number * factor
const double = createMultiplier(2) // 相当于 function(number) { return number * 2 }
console.log(double(5)) // 10
高阶函数(Higher-order Functions)
什么是高阶函数
- 接受一个或多个函数作为参数的函数
- 或返回一个函数作为结果的函数
JavaScript中的常见高阶函数
map、filter、reduce、forEach .....
js
const numbers = [1, 2, 3, 4, 5]
const sequared = numbers.map(number => number * number)
const evens = numbers.filter(number => number % 2 === 0)
const sum = sequared.reduce((total, number) => total + number, 0)
numbers.forEach(number => console.log(number))
函数组合(Function Composition)
什么是函数组合
将多个函数串联起来,形成新的功能,使代码更简洁、可读性更高
函数组合的实现方式
手动实现
js
// 简单示例
function compose(f, g) {
return function (x) {
return f(g(x));
}
}
// 多个组合示例
function compose(...funcs) {
return funcs.reduce((aFun, b) => (...args) => a(b(...args)))
}
使用函数组合库
js
const R = require('ramda');
const addOneAfterDouble = R.compose(R.add(1), R.multiply(2));
console.log(addOneAfterDouble(5)); // 11
实践应用
js
function compose(...funcs) {
return funcs.reduce((aFun, b) => (...args) => a(b(...args)))
}
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 定义处理函数
const isEven = n => n % 2 === 0;
const square = n => n * n;
const addOne = n => n + 1;
const toNegative = n => -n;
// 创建数字处理管道
const numberProcessor = compose(
toNegative,
addOne,
square
);
// 应用管道到偶数
const processedNumbers = numbers
.filter(isEven)
.map(numberProcessor);
console.log(processedNumbers); // [-5, -17, -37, -65, -101]
高级概念
柯里化(Currying)
将接受多个参数的函数转换成一系列接受单个参数的函数
js
// 普通函数
function add(a, b) {
return a + b
}
// 柯里化版本
function curriedAdd(a) {
return function (b) {
return a + b
}
}
// 使用柯里化
const add5 = curriedAdd(5)
console.log(add5(10)) // 15
// 使用Ramda实现柯里化
const R = require('ramda');
const curriedAdd = R.curry(add);
const add5 = curriedAdd(5);
console.log(add5(3)); // 8
递归(Recursion)
函数式编程中常用的控制流程,替代循环
js
function sum(arr) {
if (arr.length === 0) {
return 0;
}
return arr[0] + sum(arr.slice(1));
}
console.log(sum([1, 2, 3, 4, 5])); // 15
惰性求值(Lazy Evaluation)
只有需要时才计算表达式,有助于优化性能
js
// 惰性求值示例
class LazyArray {
constructor(generator) {
this.generator = generator;
this.operations = [];
}
filter(predicate) {
return new LazyArray(function* () {
for (const item of this.generator()) {
if (predicate(item)) {
yield item;
}
}
}.bind(this));
}
map(transform) {
return new LazyArray(function* () {
for (const item of this.generator()) {
yield transform(item);
}
}.bind(this));
}
take(n) {
return new LazyArray(function* () {
let count = 0;
for (const item of this.generator()) {
if (count >= n) break;
yield item;
count++;
}
}.bind(this));
}
// 执行所有操作并返回结果数组
evaluate() {
const result = [];
for (const item of this.generator()) {
result.push(item);
}
return result;
}
}
// 创建一个无限自然数序列的生成器
function* naturalNumbers() {
let i = 1;
while (true) {
yield i++;
}
}
// 创建惰性数组的工厂函数
function createLazyArray() {
return new LazyArray(naturalNumbers);
}
// 使用示例
const lazyArray = createLazyArray();
const result = lazyArray
.filter(x => x % 2 === 0)
.map(x => x * 2)
.take(5)
.evaluate();
console.log(result);
总结
掌握函数式编程需要循序渐进,从基础概念开始,逐步深入到高级应用。以下是关键点:
- 纯函数是函数式编程的基石
- 高阶函数是函数式编程的"语法糖"
- 函数组合让代码更简洁、可读
- 不可变数据确保代码的可预测性
- 递归是函数式编程的控制流程