在学习 JS 的过程中时常会听到一个名次——“函数式编程”,那么究竟什么是函数式编程,函数式编程又有什么优点,这就在这篇博客进行一个简单的总结吧~
主要内容:
- 函数式编程的概念
- 函数式编程的优点与示例
什么是函数式编程
首先,我们放下编程的概念,我们来看函数。
函数的概念来自于数学,数学中的函数 f(x) = y
有一个非常重要的特点对于一个给定的 x,有唯一的 y 与其对应(这就是为什么椭圆曲线不是函数)
然而在编程中,函数并不具有这个特点,举个栗子:
let val = 1function add(x){ return x + val}console.log(add(1)) // 2val += 1console.log(add(1)) // 3复制代码
可以看到编程中的函数在参数相同的情况下,允许有不同的返回值——只要依赖于函数外部的量
那么当函数依赖于外部的变量(常量不会有这样的问题),并且函数在多处调用,就有可能出现 bug,函数的调用结果可能会和预期结果相去甚远
而函数式编程就要求我们规避这样的情况,让所有函数对于相同的输入的返回值相同,这样的特性就叫做引用透明性,这就是函数式编程的核心特性!
一个符合引用透明性的函数的栗子:
function id(x){ return x; }复制代码
函数式编程带来的优点
在上文中提到过,编程语言中的函数大多是不满足数学中的函数的概念的,so 我们将满足数学函数条件的函数称为“纯函数”
函数式编程的优点大多都来自于纯函数
可测试性
除了测试人员进行的全方位测试外,我们在开发过程中往往要对自己写的代码进行模块测试
在开发中,我受非纯函数迫害已久,由于它依赖了外部变量(比如存储在 localStorage 中的数据)我不得不三番五次去检查这些外部变量是否在某个过程中被改变甚至是删除
let val = 1function add(x){ return x + val}console.log(add(1)) // 2// 在未知因素影响下 val被改变console.log(add(1)) // 预期结果 2,实际输出 emmmmm复制代码
如果我没有注意外部依赖而是一头扎进函数逻辑里,可能永远都找不到这个bug
代码的并发性
虽然我们都知道 JS 是一门单线程语言(关于JS的执行可以参见->),但是我们为了提高前端的性能可能会通过 WebWorker
来并发执行多个任务,或者在 Node 环境下 JS 并发执行函数
这个时候就是对非纯函数的一个很大的考验:
let global = "全局变量"let func1 = ()=>{ global = "全局变量被改变了" // 一些逻辑}let func2 = ()=>{ if(global = "全局变量"){ return true }}复制代码
上面的两个函数都依赖于外部的global,当它们并发执行,func1就会对func2产生影响,如果将它们变为纯函数就不会有这样的问题:
let global = "全局变量"let func1 = (x)=>{ x = "全局变量被改变了" // 一些逻辑}let func2 = (x)=>{ if(x = "全局变量"){ return true }}复制代码
函数执行的缓存
当我们的函数都是纯函数,而我们又会多次调用函数,我们就可以对函数对象进行一个缓存
比如我们需要大量计算数字的4次方,我们可以建立一个映射表用来缓存函数的执行结果:
// 映射表let fourTimesTable = {};let fourTimes = (x){ return x*x*x*x}// 检查表中是否有 2 的四次方,如果有就返回,如果没有就执行函数避免运算fourTimesTable.hasOwnProperty(2)? fourTimesTable[2]: fourTimesTable[2] = fourTimes(2)复制代码
管道与组合
管道过滤器是一种很经典的设计模式,我们可以将这种模式和函数式编程结合起来
在管道和过滤器软件体系结构中,每个模块都有一组输入和一组输出。每个模块从它的输入端接收输入数据流,在其内部经过处理后,按照标准的顺序,将结果数据流送到输出端,以达到传递一组完整的计算结果实例的目的。
在这种结构中,各模块之间的连接器充当了数据流的导管,将一个过滤器的输出传到下一个过滤器的输入端。所以,这种连接器称为“管道”。
我们也可以将这样的私用运用在函数式编程中,将一个个函数作为过滤器,通过函数的组合,形成一条数据处理的通路:
// 参数累加function add(...args){ let result = args.reduce((prev, cur, index, arr)=> { return prev + cur; }) return result}// 参数累乘function times(...args){ let result = args.reduce((prev, cur, index, arr)=> { return prev * cur; }) return result}let arr1=[1,3,6],arr2=[2,5,21],arr3=[3,7,8,27,4]// 三组数据,要求组内累乘,然后结果累加add(times(...arr1),times(...arr2),times(...arr3))// 三组数据,要求组内累加,然后结果累乘times(add(...arr1),add(...arr2),add(...arr3))复制代码
对函数式编程的初探暂止于此,进一步学习后再做总结~
本文同步发布于我的,,
如果有什么问题,意见,建议欢迎评论;如果觉得我写的不错,那就点个赞吧~