js-函数式编程(柯里化+compose)

108 阅读2分钟

1.函数柯里化

//函数的柯里化:复用重复参数的部分,可以使得其余不一致参数,基于重复参数所得到的函数进行调用
//常见场景:
// let baseFn = fn(1, 2);
// let res1 = baseFn(6);
// let res2 = baseFn(8);
// 1.返回一个函数(直到所有参数全部传递完毕,才会真正执行fn)
function curry(func) {

    return function curried(...args) {
      if (args.length >= func.length) {
        return func.apply(this, args);
      } else {
        return function(...args2) {
          return curried.apply(this, args.concat(args2));
        }
      }
    };
  
  }
  
  function sum(a, b, c) {
    return a + b + c;
  }
  
  let curriedSum = curry(sum);
  
  alert( curriedSum(1, 2, 3) ); // 6,仍然可以被正常调用
  alert( curriedSum(1)(2,3) ); // 6,对第一个参数的柯里化
  alert( curriedSum(1)(2)(3) ); // 6,全柯里化
  

易混淆题目:对fn.toString进行重写

参考文章:juejin.cn/post/684490…

问题:add(1)(2) add(1)(3)(4)...实现无限极累加,实现累加器


//   累加器(fn.toString+闭包)
const add=(arg1)=>{
    let args=[arg1]
    const fn=arg2=>{
        args.push(arg2);
        return fn //自己返回自己,才能实现add的无限执行
    }
    fn.toString=function(){
        return args.reduce((prev,item)=>prev+item,0)
    }
    return fn;
}
console.log(add(2)) //alert可以得到结果2
console.log(add(2)(1)) //alert可以得到结果3

2.函数编排compose

//如何实现函数编排:常见场景:一个函数的返回结果是另一个函数的参数
function TshirtNum(num) {
  return num + 100;
}
function disCount(num) {
  return num * 0.8;
}

function shoesNum(num) {
  return num * 3;
}

function asyncNum(num) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(num * 0.8);
    }, 1000);
  });
}

function asyncShoesNum(num) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(num * 3);
    }, 0);
  });
}

//常规使用:若函数嵌套过多,则可读性很差
let discountNum = disCount(100);
let shoes = shoesNum(discountNum);
let tshirt = TshirtNum(discountNum);
console.log(tshirt);


/**
 * compose的同步实现
 * */
const compose = (fnArr) => (startNum) =>
  fnArr.reduce((total, item) => item(total), startNum);
//分析:
// 1.compose返回的是一个函数,且接收一个初始值
//reduce以初始值为起点,每次item(total)执行所传递的参数都是上一次函数执行的结果
let price1 = compose([disCount, shoesNum]);
console.log(price1)




/**
 * compose的异步实现
 * */
const composeAsync = (fnArr) => (startNum) =>
  fnArr.reduce((total, item) => {
    console.log(total, item);
    return total.then(item);
  }, Promise.resolve(startNum));

let price = composeAsync([asyncNum, shoesNum]);
console.log("******", price(100));
price(100).then((res) => {
  console.log("***", res); //一秒之后,打出结果
});

compose的特殊效果:koa-compose(应用考点)

题目:按照顺序执行

//洋葱形式的执行
//要求执行顺序由内到外,退出时再由外到内
function TshirtNum(num, next) {
  console.log("starting TshirtNum");
  next(num + 100);
  console.log("end TshirtNum");
}
function disCount(num, next) {
  console.log("starting disCount");
  next(num * 0.8);
  console.log("end disCount");
}

function shoesNum(num, next) {
  console.log("starting shoesNum");
  next(num * 3);
  console.log("end shoesNum");
}
//要求:想要执行顺序依次为:
// starting TshirtNum
// starting disCount
// starting shoesNum
// end shoesNum
// end disCount
// end TshirtNum

按照以上要求,compose手写实现:

function compose(arr) {
  return function (startNum) {
    let result;
    let dispatch = function (i, ctx) {
      let fn;

      if (i < arr.length) {
        fn = arr[i];
      }
      console.log("*********", [fn], ctx);
      if (i === arr.length) {
        result = ctx;
        return;
      }
      //注意:bind会返回一个函数
      return fn(ctx, dispatch.bind(null, ++i));
    };
    dispatch(0, startNum); //初始执行
    return result;
  };
}

let composeFn = compose([TshirtNum, disCount, shoesNum]);
console.log("最终结果:", composeFn(100));

执行结果:

image.png