js基础篇-柯里化原理及实现

2,501 阅读2分钟

前言

柯里化(Currying): 是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术.

柯里化是很多高级编程语言拥有的特性,比如:JS、scala。

柯里化的好处:

1、参数复用。

2、提前返回。

3、 延迟计算/运行。

什么是柯里化

函数柯里化(currying)又称部分求值。一个 currying 的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。

面试的时候: 是不是经常遇到面试题,要求实现:

sum(1,2,3);// 6
sum(1,2)(3);// 6
sum(1)(2,3);// 6

这类面试题,这个实现后面会详解

先举两个例子:

  function add(num1, num2){
      return num1+num2;
  }
  function curriedAdd(num2) {
      return add(5, num2);
  }
  console.log(add(2,3));//5
  console.log(curriedAdd(3));

从技术上来说 curriedAdd 并不是柯里化的函数,但是很好展示其概念

2.实现函数只执行一次

  function onesFtn(fun) { 
      let flag = true; 
      return function() { 
          if (flag) { 
              fun && fun(); 
              flag = false; 
          } 
      } 
  } 
  let onceConsole = onesFtn(()=> { 
      console.log('只执行一次'); 
  });
  onceConsole(); 
  onceConsole();

这个是不是很熟悉,闭包不就是这个吗,对了,柯里化就是以闭包为基础。

柯里化通用实现:(javaScript高级程序设计 第三版 604页)

function curry (fn) {
    var args =Array.prototype.slice.call(arguments, 1);// 获取所有参数(除第一个外)的数组
    return function (){
        var innerArgs = Array.prototype.slice.call(arguments);
        var finalArgs = args.cocat(innerArgs);
        return fn.apply(null,finalArgs);// 这个函数并没有考虑到执行环境,所以apply第一个参数是null,可以对比bind函数实现来看
    }
}

第一个例子 可以改成

 function add(num1,num2) {
     return n1+n2;
 }
 var cur =curry(add,5);
 console.log(cur,3); 

这样是不是就明白了

同学们有没有发现这个函数很有局限性,参数不够自由,只能传2个

如:

  var addFtn = curry(function(a,b,c){
  return a+b+c;
  })
  addFtn(2)(3);////TypeError: addFun(...) is not a function

改进如下:es6写法

function curry (func) {
  const curriedFn = (...args) => {
    // 判断实参、形参个数
    if (args.length < func.length) {
      return (...args1) => {
        // 这个func用来获取arguments
        //下面三种写法都可以
        // return curriedFn(...args.concat(Array.from(args1)));
        // return curriedFn(...args.concat(...args1));
        return curriedFn(...args.concat(args1));
      }
    }
    return func(...args);
  }
  return curriedFn;
 }

 const getSum = (a, b, c) => {
  return a + b + c;
 };
 const curried = curry(getSum);

 console.log(curried(1, 2, 3));
 console.log(curried(1,2)(3));
 console.log(curried(1)(2, 3));
 console.log(curried(1)(2)(3));

再精简一下:

 const curry = (func) => {
  return curriedFn = (...args) => {
    // 判断实参、形参个数
    if (args.length < func.length) {
      return (...args1) => {
        // 这个func用来获取arguments
        //下面三种写法都可以
        // return curriedFn(...args.concat(Array.from(args1)));
        // return curriedFn(...args.concat(...args1));
        return curriedFn(...args.concat(args1));
      }
    }
    return func(...args);
  }
 }

 const getSum = (a, b, c) => {
  return a + b + c;
 };
 const curried = curry(getSum);

 console.log(curried(1, 2, 3));
 console.log(curried(1,2)(3));
 console.log(curried(1)(2, 3));
 console.log(curried(1)(2)(3));

讲到这,干脆再写一篇函数绑定bind实现