纯函数
纯函数概念
- 纯函数:
相同的输入永远会得到相同的输出
,而且没有任何可观察的副作用- 纯函数就类似数学中的函数(用来描述输入和输出之间的关系)y=f(x)
- lodash 是一个纯函数的功能库,提供了对数组、数字、对象、字符串、函数等操作的一些方法
- 数组的 slice 和 splice 分别是:纯函数和不纯的函数
- slice 返回数组中的指定部分,不会改变原数组
- splice 对数组进行操作返回该数组,会改变原数组
- 函数式编程不会保留计算中间的结果,所以变量是不可改变的(无状态的)
- 我们可以把一个函数的执行结果交给另一个函数去处理
- 代码示例
let arr = [1,2,3,4,5,6]
// 纯函数 --- 相同的输入永远会得到相同的输出
console.log(arr.slice(0,3))
console.log(arr.slice(0,3))
console.log(arr.slice(0,3))
// 非纯函数 --- 相同的输入不会得到相同的输出
console.log(arr.splice(0,3))
console.log(arr.splice(0,3))
console.log(arr.splice(0,3))
// 自己封装一个纯函数
function getSum (n1, n2) {
return n1 + n2
}
console.log(getSum(0,1))
console.log(getSum(1,2))
console.log(getSum(2,3))
- 我们在基于函数式编程的过程中,我们经常会需要一些细粒度的纯函数,自己去写的话要写非常多,会非常的不方便,但我们不需要特别担心,我们有很多的函数式编程的库,比如说Lodash
- 当我们有了这些细粒度的函数后,我们后边在学函数组合的时候,我们可以把这些细粒度的函数组合在一起,组合出一个更强大的函数,当我们在调用这些函数的过程中,我们就可以把一个函数的执行结果交给另一个函数去处理
Lodash
-
官方介绍:现代化的使用的js库,提供了模块化、高性能以及一些附加的功能
-
之前很多人把Lodash当成一个功能库来使用,当es5、es6给原生js增加了很多方便的方法以后,就有很多人来讨论,是否还有使用Lodash的必要,其实Lodash提供了常用的便捷方法以外,还提供了跟函数式相关的便捷方法,比如说函数的柯里化、函数组合等,之后我们会学习到
-
使用
- npm init -y 初始化一个package.json文件
- npm i lodash 下载lodash
- 使用require引入lodash:
const _ = require('lodash')
纯函数的好处
- 可缓存
- 因为纯函数对相同的输入始终有相同的结果,所以可以把纯函数的结果缓存起来
- 举个例子:如果有个函数或者方法,执行起来很耗时间,但是我们又需要多次使用,这种情况对性能是有影响的,那我们想要提高这个性能的话,当这个函数第一次调用的时候将结果缓存下来,当我们第二次在调用的时候,我们不需要在等待那么长的时间,而是直接从缓存中获取结果,从而提高性能
- 示例
const _ = require('lodash') function getArea (r){ console.log(r); return Math.PI * r * r } // 通过Lodash的memoize来查看纯函数的可缓存性 let getAreaWithMemory = _.memoize(getArea) console.log(getAreaWithMemory(4)) console.log(getAreaWithMemory(4)) console.log(getAreaWithMemory(4)) // 模拟memoize 方法的实现 function memoize (fn) { // 存储函数的执行结果 let cache = {} return function () { // 作为键值对中的键 let key = JSON.stringify(arguments) /** * 通过key键来判断cache中是否有缓存的结果, * 如果没有,执行传递进来的函数, * 如果有直,接返回 * * apply可以改变函数内部的this * 第二个参数可以把一个伪数组或者一个数组展开然后将每一项传递给调用者 * */ cache[key] = cache[key] || fn.apply(fn,arguments) return cache[key] } } let getAreaWithMemory = memoize(getArea) console.log(getAreaWithMemory(4)) console.log(getAreaWithMemory(4)) console.log(getAreaWithMemory(4))
- 可测试
- 因为我们的纯函数,始终有输入和输出,而单元测试,其实就是在测试这个函数的结果,所以我们的纯函数都是可测试的函数
- 并行处理
- 在多线程环境下并行操作共享的内存数据很可能会出现意外情况
- 纯函数不需要访问共享的内存数据,所以在并行环境下可以任意运行纯函数(Web Worker)
函数的副作用
- 纯函数:对于相同的输入永远会得到相同的输出,而且没有任何可观察的副作用
// 不纯的 --- 判断条件mini是全局变量,一旦被修改,就不能满足纯函数的概念
let mini = 18
function checkAge (age) {
return age >= mini
}
// 纯函数(有硬编码,后续可以通过柯里化解决)
function checkAge (age) {
let mini = 18
return age >= mini
}
副作用让一个函数变得不纯(如上例),纯函数根据相同的输入返回相同的输出,如果函数依赖于外部的状态就无法保证输出相同,就会带来副作用。
-
副作用来源:
- 配置文件
- 数据库
- 获取用户的输入
- …
所有的外部交互都有可能带来副作用,副作用也是的方法通用性下降不是和扩展和可重用性,同时副作用会给程序中带来安全隐患给程序带来不确定性,但是副作用不可能完全禁止,尽可能控制他们在可控范围内发生