一、纯函数
1、概念
1)此函数在相同的输入值时,需产生相同的输出;
2)函数的输出和输入值以外的其他隐藏信息或状态无关,也和由I/O设备产生的外部输出无关;
3)该函数不能有语义上可观察的函数副作用,如“触发事件”,使输出设备输出,或更改输出值以外物件的内容等;
4)确定输入,一定会产生确定的输出;
5)函数执行过程中,不能产生任何副作用;
注:在计算机科学中,副作用的概念,表示在执行一个函数时,除了返回函数值之外,还对调用函数产生 了附加的影响,比如修改了全局变量,修改参数或者改变外部的存储。
2、纯函数理解
1、判断是否时一个纯函数
let obj = {name: '小明', age: 10}
/**
* 判断下面两个那个时纯函数?
* fun 不是
* test 是
*/
// fun 会修改 obj 对象的本身
function fun(info){
info.age = 100
}
fun(obj)
// test 不会修改对象本身,并且 会返回一个新的对象
// 当你给 test 中传入相同的对象时,他的返回值也是相同的,这就符合纯函数的定义
function test(info){
return {
...info,
age: info.age + 1
}
}
test(obj)
test(obj)
注:在开发过程中,使用非纯函数,他往往就会成为 bug 的温床。
二、柯里化
给函数传递一部分参数来调用这个函数,并且 当前的这个函数他会在返回一个函数,去处理剩余的参数,见下面 示例:
也就是 拆分函数的参数,这样的就可以叫 柯里化
。
1、使用函数柯里化的好处?
在函数式的编程中,尽量 让一个函数处理一个问题,尽可能的单一,而不是将一些问题交给一个方法去解决,就好比 react 的函数式组件,你的一个功能组件是经过多个 UI 组件合成而来的一样,所以我们可以将每次传入的参数在,当前函数中处理,在当前函数处理完成后,在下一个函数中在使用上一个函数的处理结果即可。
2、例子
实现一个给第一个参数加2,给第二个参数乘2的例子
3、柯里化函数的实现
注:也就是把多个参数的函数转换成柯里化函数,这是一个可以作为公共方法的函数。
/**
* 多参函数 转 柯里化函数
*/
function currying(fn) { // 接受一个函数,返回一个函数
// 这里是接受剩余参数
function receivedParameters(...args) {
// 判断当前已经接受到的参数的个数, 和参数本身(这个参数本身就是一个函数)需要接收到参数是否已经一致
// console.log(fn.length);
// console.log(args.length);
// 到传入函数的参数(fn 的参数) 大于 传入的参数(...args)时,就直接调用传入的函数
if (args.length >= fn.length) {
// 这个 apply 是为了方式外面调用时,绑定了this,而导致这个里面执行 fn 时的混乱
return fn.apply(this, args);
} else {
// 当参数不够的时候, 需要返回一个函数,来接续接收传入的参数,所以 在这 就需要把传入的所有参数,进行一次拼接
return function (...smallArgs) {
// 这个里面用了递归,来检查参数 是否达到,达到了,就运行传入的函数(fn)
return receivedParameters.apply(this, [...args, ...smallArgs])
}
}
}
return receivedParameters
}
// 使用
function fun(n, m, b) { // fun的参数个数,是可以 通过 fun.length 拿到的
return n + m + b
}
const test = currying(fun);
console.log(test(1, 2, 3));
console.log(test(1)(2, 3))
console.log(test(1)(2)(3));
三、组合函数
当要获取的结果,是多个纯函数共同运算获得的,这个时候就可以那这几个陈函数组合起来,一次进行调用,这就叫组合函数,它可以减少代码的冗余。
function add(num){
return num + 10
}
function multiply(num){
return num * 2
}
const num = add(multiply(2));
console.log(num);
// 下面开始组合上面的两个函数
function composeFn(fn1, fn2){
return function(props){
return fn1(fn2(props))
}
}
const newFn = composeFn(add, multiply);
console.log(newFn(2));
使用:
/**
* 组合函数
* @param {...any} fns 函数
*/
function composeFn(...fns){
for(let i = 0; i < fns.length; i++){
if(typeof fns[i] !== 'function'){
throw new TypeError(`${fns[i]} 不是函数类型`);
}
}
/**
* 参数
* 下面的难点 是 apply 、call 的使用
*/
return function(...args){
let index = 0; // 从零开始
let result = fns.length ? fns[index].apply(this, args) : args; // 判断是否有函数参数传入
while(++index < fns.length){
result = fns[index].call(this, result);
}
return result;
}
}
function add1(num){
return num + 10
}
function add2(num){
return num + 1
}
console.log(add2(add1(1))); // 原始的使用
const test = composeFn(add1, add2);
console.log(test(1));