前言
面试中遇到一部分人对函数式编程不了解,还有一部分认为函数式编程除了面试中能用到,在自己的项目经验中很难看到具体的应用场景。
函数式编程作为一种编程范式由来已久。函数式编程强调不可变性和纯函数,即函数的输出仅由输入决定,没有副作用和对外部状态的依赖。这种特性使得函数式代码更易于理解、测试和调试。
函数式编程强调使用多个函数 来组合 和来处理数据。其核心是将运算过程抽象成函数,好处是可以复用。
本文将尝试用通俗易懂的方式讲解函数式编程在前端项目中的应用。会对函数式编程进行简单的"扫盲",然后结合几个实例方便大家理解。
认识函数式编程
在开始之前,请先看一个函数式编程的实例。
js
// 纯函数,只做一件事,输入输出,没有副作用
function double(number) {
return number * 2;
}
function square(number) {
return number * number;
}
// 不改变原来的数据,创建新的数组
function doubleArray(arr) {
return arr.map(double);
}
function squareArray(arr) {
return arr.map(square);
}
// 把多个函数组合成一个新的函数
function compose(...fns) {
return function (x) {
return fns.reduceRight(function(acc, fn){
return fn(acc);
}, x);
};
}
const numbers = [1, 2, 3, 4, 5];
const processArray = compose(squareArray, doubleArray);
const result = processArray(numbers);
console.log(result); // [ 4, 16, 36, 64, 100 ]
有没有发现,函数式编程在实践中就像用乐高堆积木。
在这个例子中,先定义了两个纯函数。遵循不可变性原则对需要运算的数组进行新建而不是改变原有数据。最后把多个功能函数组合成一个新的函数,实现需要的功能。
纯函数
上面的实例中定义了两个纯函数 double
和 square
。那么什么样的函数是纯函数呢?
根据定义,如果一个函数符合两个条件,它就可以被称为纯函数:
- 此函数在相同的输入值时,总是产生相同的输出。
- 次函数不会对除了函数返回值之外的任何东西产生可观察的副作用。不会修改传入的参数,也不会对全局状态进行更改,不会进行 I/O 操作,也不会触发网络请求等
由此可见,纯函数的执行仅仅是为了计算并返回一个值。
下面是一个纯函数和非纯函数的对比实例。
js
// 纯函数,不依赖于外部状态,相同的输入总是产生相同的输出
function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // 输出 5
console.log(add(2, 3)); // 输出 5
// 非纯函数,依赖于外部状态,相同的输入可能产生不同的输出
let result = 0;
function addToResult(value) {
result += value;
return result;
}
console.log(addToResult(5)); // 输出 5
console.log(addToResult(3)); // 输出 8
函数是一等公民
在函数式编程中,函数被视为一等公民。函数可以像其他数据类型(如整数、字符串等)一样被操作、传递和赋值。这种特性使得函数式编程具备更高的抽象能力和灵活性,可以更方便地实现函数的组合和重用。
1、函数可以被赋值给变量
js
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
const operation = add; // 函数赋值给变量
console.log(operation(2, 3)); // 输出: 5
operation = multiply; // 可以修改变量的值为另一个函数
console.log(operation(2, 3)); // 输出: 6
2、函数可以作为参数传递
js
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
const applyOperation = (a, b, operation) => operation(a, b);
console.log(applyOperation(2, 3, add)); // 输出: 5
console.log(applyOperation(2, 3, multiply)); // 输出: 6
3、函数可以作为返回值返回
js
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
const createOperation = (operator) => {
if (operator === '+') {
return add;
} else if (operator === '*') {
return multiply;
}
};
const operation1 = createOperation('+');
console.log(operation1(2, 3)); // 输出: 5
const operation2 = createOperation('*');
console.log(operation2(2, 3)); // 输出: 6
函数组合
函数式编程中还有一个重要的思想就是组合(compose)
,它允许我们把逻辑通过函数的形式进行拆分,然后进行函数之间的组合最后得到想要的完整逻辑。文章开篇的实例中已经进行了演示,这里不做重复。
简言之函数式编程的实践就是对业务逻辑进行拆分,变成一个个纯函数,然后在组合成一个完整的逻辑模块。
这样做的好处很明显,简化逻辑,复用代码,方便写单元测试。
函数式编程库推荐
JavaScript 函数式编程的库有很多,以下是几个常用的库:
Ramda
Ramda 是一个专注于函数式编程的 JavaScript 库,提供了许多实用的函数和工具,用于支持函数式编程的开发风格。它强调不可变性、纯函数和柯里化,并提供了丰富的函数来处理和操作数据。
Lodash/fp
Lodash 是一个广泛使用的 JavaScript 实用工具库,而 Lodash/fp 则是 Lodash 的函数式编程版本。它提供了许多函数式风格的函数,并采用自动柯里化和数据优先的方式。
Immutable.js
Immutable.js 是一个用于处理不可变数据的 JavaScript 库。它提供了一组持久化的数据结构,如 List、Map、Set 等,以及一些用于操作这些数据结构的函数。通过使用 Immutable.js,可以更轻松地管理和操作不可变数据,避免副作用和数据修改。
RxJS
RxJS 是一个响应式编程库,它基于观察者模式和可观察序列(Observables)的概念。它提供了丰富的操作符和工具,用于处理异步数据流和事件序列。RxJS 支持函数式编程的思想,提供了一种声明式和组合的方式来处理异步操作。
总结
本文简单介绍了函数式编程的基本概念和特点,包括纯函数、函数是一等公民和函数组合。旨在能够让大家方便快速的理解函数数编程是什么。函数式编程可以提高代码的可读性、可维护性和并发性,非常值得学习和应用。