详解JavaScript函数式编程中的curry函数

本文详细介绍了JavaScript中的curry函数,包括柯里化的概念、curry函数应满足的要求,以及如何实现一个完整的curry函数,包括占位符的处理和参数完备性的判断。通过实例和代码解析,帮助读者深入理解curry函数的工作原理。
摘要由CSDN通过智能技术生成

curry函数在JavaScript函数式编程中十分重要。在网上搜索该函数,现有的基本上都不是我想要的;分析ramda,lodash等JavaScript函数式库,发现该函数的实现十分复杂,一时半会摸不清头绪。于是昨天晚上花了几个小时,自己实现了该函数,今天把它拿出来让大家参考一下。

柯里化函数

柯里化概念

所谓的柯里化就是把一个多参数的函数转换为一个嵌套的单参数函数的过程,它要求使用部分参数时返回一个新的函数,
在真正运行之前等待外部提供剩余参数,当参数准备完备后执行最初的多参数函数。

柯里化函数应该满足的要求

  1. 从柯里化的概念我们可以得出柯里化函数(即curry函数)应该满足以下要求:
    • curry函数接受一个多参数函数作为参数
    • curry调用后返回一个被柯里化的函数
    • 被柯里化的函数调用时一次可以传入一个参数,也可以传入多个参数
    • 被柯里化的函数调用后如果没有得到足够的参数,那么会返回一个新的函数
    • 被柯里化的函数调用后如果得到了足够的参数,那会就会执行最初的那个多参数函数。

一个简单的柯里化函数实例

  1. 知道柯里化的概念,和柯里化函数应满足的要求后,我们实现一个简单的柯里化函数,来为后面真正curry函数的实现打下基础。
  2. 假设我们有一个函数sum,它的作用是求两个数字的和。sum函数的实现如下
       const sum = (x, y) => x + y;
    
  3. 实现简单的curry函数:
        const curry = (fn) => {
         
            return function recursive(...args) {
         
                // 如果args.length >= fn.length则表明传入了足够的参数,此时调用fn并返回
                if (args.length >= fn.length) {
         
                    return fn(...args);
                }
        
                // 否则表明没有传入足够的参数,此时返回一个函数,用这个函数接受后面传递的新参数
                return (...newArgs) => {
         
                    // 递归调用recursive函数,并返回
                    return recursive(...args.concat(newArgs));
                };
            };
        };
    
  4. 将sum函数柯里化,并测试
        const cSum = curry(sum); // => [Function]
        const cSum1 = cSum(1); //=> [Function]
        cSum1(2);  // => 3
    
  5. 到此为止我们实现了一个简单的柯里化实例。那么我们用来柯里化sum的curry函数在执行过程中到底做了什么呢?
    • 先让我们打印输出一下console.log(cSum.toString());发现得到下面的结果:
         function recursive(...args) {
             // 如果args.length >= fn.length则表明传入了足够的参数,此时调用fn并返回
             if (args.length >= fn.length) {
                 return fn(...args);
             }
      
             // 否则表明没有传入足够的参数,此时返回一个函数,用这个函数接受后面传递的新的参数
             return (...newArgs) => {
                 // 递归调用recursive函数,并返回
                 return recursive(...args.concat(newArgs));
             };
         }
      
      于是我们可以知道执行curry(sum)返回了一个函数
    • 再让我们打印输出一下console.log(cSum(1).toString());发现得到下面的结果:
          (...newArgs) => {
                  // 递归调用recursive函数,并返回
                  return recursive(...args.concat(newArgs));
              }
      
      于是我们可以知道调用cSum(1)时参数没有准备完备,返回了一个函数,这个函数内部递归调用了recursive函数,并且recursive函数的参数是1和我们调用cSum1时传入的参数。
    • 当我们执行cSum1(2)得到了结果 3,这表明参数准备完备了,这时执行了下面的代码,从而得到了我们想要的结果。
          // 如果args.length >= fn.length则表明传入了足够的参数,此时调用fn并返回
          if (args.length >= fn.length) {
              return fn(...args);
          }
      

柯里化函数的实现

我们要实现什么?

上面我们实现了一个简单的curry函数,用来柯里化sum函数;但实际情况要复杂的多。我们要柯里化的函数的参数是不确定的,有时也希望能够通过占位符表示将要传递的参数;因此我们真正要实现的curry函数将满足以下两个条件:

  • 可以柯里化具有任意数量参数的函数
  • 可以通过占位符表示将要传递的参数

实现真正的curry函数

一、占位符
  1. 这里我们通过 __(双下划线)来表示占位符,它的值是一个对象,具体如下所示:
    const __ = {
         '@@/placeHolder': true}; // curry  函数的占位符
    
  2. 判断一个标识符是否是占位符,当一个标识符不为null,且是一个对象,且对象中的'@@/placeHolder'属性全等于 true则认为它是一个占位符。通过函数isPlaceHolder来实现该功能:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值