初见函数式编程

在学习 JS 的过程当中时常会听到一个名次——“函数式编程”,那么究竟什么是函数式编程,函数式编程又有什么优势,这就在这篇博客进行一个简单的总结吧~html

主要内容:前端

  • 函数式编程的概念
  • 函数式编程的优势与示例

什么是函数式编程

首先,咱们放下编程的概念,咱们来看函数。git

函数的概念来自于数学,数学中的函数 f(x) = y 有一个很是重要的特色对于一个给定的 x,有惟一的 y 与其对应(这就是为何椭圆曲线不是函数)github

然而在编程中,函数并不具备这个特色,举个栗子:编程

let val = 1
function add(x){
    return x + val
}
console.log(add(1)) // 2
val += 1
console.log(add(1)) // 3
复制代码

能够看到编程中的函数在参数相同的状况下,容许有不一样的返回值——只要依赖于函数外部的量设计模式

那么当函数依赖于外部的变量(常量不会有这样的问题),而且函数在多处调用,就有可能出现 bug,函数的调用结果可能会和预期结果相去甚远缓存

函数式编程就要求咱们规避这样的状况,让全部函数对于相同的输入的返回值相同,这样的特性就叫作引用透明性,这就是函数式编程的核心特性!并发

一个符合引用透明性的函数的栗子:编程语言

function id(x){ return x; }
复制代码

函数式编程带来的优势

在上文中提到过,编程语言中的函数大可能是不知足数学中的函数的概念的,so 咱们将知足数学函数条件的函数称为“纯函数”函数式编程

函数式编程的优势大多都来自于纯函数

可测试性

除了测试人员进行的全方位测试外,咱们在开发过程当中每每要对本身写的代码进行模块测试

在开发中,我受非纯函数迫害已久,因为它依赖了外部变量(好比存储在 localStorage 中的数据)我不得不三番五次去检查这些外部变量是否在某个过程当中被改变甚至是删除

let val = 1
function add(x){
    return x + val
}
console.log(add(1)) // 2
// 在未知因素影响下 val被改变
console.log(add(1)) // 预期结果 2,实际输出 emmmmm
复制代码

若是我没有注意外部依赖而是一头扎进函数逻辑里,可能永远都找不到这个bug

代码的并发性

虽然咱们都知道 JS 是一门单线程语言(关于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))
复制代码

对函数式编程的初探暂止于此,进一步学习后再作总结~

本文同步发布于个人我的博客CSDN掘金

若是有什么问题,意见,建议欢迎评论;若是以为我写的不错,那就点个赞吧~

相关文章
相关标签/搜索