纯函数
函数式编程中有一个很重要的概念叫做纯函数(Pure Function),Javascript符合函数式编程的范式,也是有纯函数的。什么叫做纯函数呢?维基百科是这样定义的:
在程序设计中,若一个函数符合以下条件,那么这个函数被称为纯函数:
此函数在相同的输入值时,需产生相同的输出。
函数的输出和输入值以外的其他隐藏信息或状态无关,也和由I/O设备产生的外部输出无关。
该函数不能有语义上可观察的函数副作用,诸如“触发事件”,使输出设备输出,或更改输出值以外物件的内容等。 总结一下就是两点:
- 确定的输入有确定的输出
- 函数执行过程中不能产生
副作用那么什么又叫副作用呢?这个词最早是来自于医学的一个概念,比如你吃治疗一种病的药又会导致另一种病。在计算机科学中的副作用也是这个概念,比如你执行了某个函数,但是这个函数改变了全局的某个变量,那么这就是副作用,这个函数就不能称为纯函数。举一个栗子:
const arr = [1, 2, 3, 4]
arr.slice(0, 3)
arr.splice(2)
slice这个函数执行有确定的参数0,3,也有确定的输出[1, 2, 3],而没有改变本来的数组arr,所以说slice就是一个纯函数。反之splice(2)函数执行却改变了arr数组的长度,所以不是纯函数 。
const obj = {
name: 'zengge'
}
function foo(obj) {
obj.name = 'zengdada'
}
这个foo函数执行改变了参数obj的值,所以它也不是一个纯函数。
那么纯函数的优势是什么呢?
- 保证了函数的纯度,可以单纯实现自己的业务逻辑而不用担心是否修改了外部的变量。
- 输入的内容不会被修改,有
确定的输入,确定的输出。 那么纯函数有什么实际应用呢?最常用的就是运用在函数柯里化中。
柯里化
柯里化(Currying)是函数式编程中一个非常重要的概念。我们直接来看一段代码:
function compute(x, y, z) {
x += 2
y *= 2
z *= z
return x + y + z
}
const res = compute(1, 2, 3)
console.log(res)
function curry(x) {
x += 2
return function(y) {
y *= 2
return function(z) {
z *= z
return function() {
return x + y + z
}
}
}
}
const res = curry(1)(2)(3)
console.log(res)
我们实现一个运算函数compute,输入参数1,2,3,得到结果1 + 2 + 2 x 2 + 3 x 3 = 16。并不复杂是吧,那为什么要写这么复杂的curry函数呢?
其实在工程化项目中,我们希望函数的职责应该尽可能的单一,而不是将一大堆的处理过程交给一个函数来处理。所以我们尽可能是将某一个很复杂的问题分解成一个个很简单的问题来处理,最后再将这些问题处理的结果结合起来,这在设计模式中也叫单一职责链模式。在上面的curry的函数中,我们每一个括号中的逻辑都是独立的。这就是逻辑的拆解,方便与之后更好的复用。
函数柯里化的实现
function myCurry(fn) {
function curried(...args) {
// 判断当前已经接收的参数个数是否与参数本身需要接收的参数是否一致
if(args.length >= fn.length){
return fn.apply(this, ...args)
}else {
// 若当前接收参数个数小于本身需要接收的参数,继续来接收参数
function curried2(...args2) {
// 递归执行柯里化函数,并将参数合并
return curried.apply(this, [...args, ...args2])
}
return curried2
}
}
return curried
}