【前端进阶】 纯函数到底是什么黑科技?

739 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

前言

到目前为止,我们已经回顾了函数式编程、闭包、高阶函数等函数相关的概念,接下来我们要给大家介绍纯函数Loadsh纯函数的好处以及纯函数的副作用

往期

纯函数

纯函数的概念

相同的输入永远会得到相同的输出,而且没有任何可观察的副作用

常用的纯函数进行解析

我们通过数组的slicesplice来进行解析

  • 数组的slicesplice分别是:纯函数和不纯的函数
    • slice 返回数组的指定部分,不会改变数组
    let array = [1, 2, 3, 4, 5, 6, 7, 8]
    console.log(array.slice(0,3)) // [1, 2, 3]
    console.log(array.slice(0,3)) // [1, 2, 3]
    console.log(array.slice(0,3)) // [1, 2, 3]
    
    三次的输出结果是相同的,我们回忆一下纯函数的概念:相同的输入(参数)永远会得到相同的输出,所以slice是纯函数。
    • splice对数组进行操作返回该数组,会改变原数组
    let array = [1, 2, 3, 4, 5, 6, 7, 8]
    console.log(array.splice(0,3)) // [1, 2, 3]
    console.log(array.splice(0,3)) // [4, 5, 6]
    console.log(array.splice(0,3)) // [7, 8]
    
    三次输出的结果都是不相同的,所以splice是不纯的函数。

我们自己写一个纯函数

function getSum (n1, n2) {
    return n1 + n2
}
console.log( getSum(1,2) ) // 3
console.log( getSum(1,2) ) // 3
console.log( getSum(1,2) ) // 3
  • 函数式编程是不会保留中间的结算结果,所以变量是不可变的(无状态的)
  • 我们可以把一个函数的执行结果交给另一个函数去做处理

Lodash

Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库。

纯函数的好处

  • 可缓存

    • 因为纯函数对相同的输入始终由相同的结果,所以可以把结果存储起来

      示例: 用求圆面积进行案例输出

      使用函数库: lodash的memoize

      // 记忆函数
      const _ = require('lodash')
      function getArea(r) {
          console.log(r)
          return Math.PI * r * r
      }
      let area = _.memoize(getArea)
      console.log(area(4))
      console.log(area(4))
      console.log(area(4))
      

      因为我们的函数比较简单所以这里我们在 getArea中做出输出进行查看

      4
      50.26548245743669
      50.26548245743669
      50.26548245743669
      

      可以很明显看出,我们对圆的半径输出仅仅一次,那我们该怎么实现memoize这个方法呢

    • 模拟memoize实现

      function memoize(f) {
          // 我们需要内部定义一个对象,存储起来
          let cache = {}
           // 我们在返回结果的时候,我们需要先进行判断
           // cache中是否已经有了该返回的结果了,有的话直接返回,没有的话执行一下方法并存储函数执行结果
          return function() {
              // arguments是伪数组
              let key = JSON.stringify(arguments)
              cache[key] = cache[key] || f.apply(f,arguments)
              return cache[key]
          }
      }
      

      ok,到这里我们的memoize已经完毕了,测试一下

      let area = memoize(getArea)
      console.log(area(4))
      console.log(area(4))
      console.log(area(4))
      

      输出结果:

      4
      50.26548245743669
      50.26548245743669
      50.26548245743669
      

      ok,输出结果是一样的,说明我们的思路没有问题

  • 可测试

    • 纯函数让测试更方便:纯函数始终有输入和输出方便单元测试
  • 并行处理

    • 在多线程环境下并行操作共享的内存数据很可能会出现意外情况
    • 纯函数不需要范围内共享的内存数据,所以在并行环境下可以任意运行纯函数(Web Worker),会有同学说诶js不是单线程嘛???你在逗我,es6之后,嘿嘿了解一下Web Worker不过大多数情况下还是单线程,稍微了解了解哈!

纯函数的副作用:

// 不纯的
let mini= 18
function checkAe(age) {
    return age >= mini
}
// 正经纯函数
function checkAge(age) {
    let mini = 18
    return age >= mini
}

副作用会让一个函数变得不纯(如上例),纯函数的根据相同的输出,如果函数依赖于外部的状态就无法保证相同的输出,就会带来副作用。

副作用的来源

  • 配置文件
  • 数据库
  • 获取用户的输入 所有的外部交互都有可能带来副作用,副作用也使得方法通用性下降,不适合拓展和可用性;同时副作用会给程序中带来安全隐患给程序带来不确定性,但是副作用是不能完全禁止的,尽可能的控制他们在安全可控的范围。

总结

  • 纯函数是相同的输入永远会得到相同的输出,而且没有任何可观察的副作用,而且可以在一定程度提高性能,存储相同的输入的数据,不过我们需要注意副作用带来的一些安全隐患哦!

src=http___dingyue.ws.126.net_1kNX5UOlvvZhqzVi4eMVTefclpP9NYP6nSIQPkU9McOAq1553651252093compressflag.jpg&refer=http___dingyue.ws.126.jpg