ES6一行代码实现函数柯里化的完整推导过程

作者:小括号

时间:2026-10-23

浏览:398

本文详细解析了JavaScript中函数柯里化的概念,并通过从基础实现到ES6语法的逐步优化,最终展示如何用一行代码实现柯里化函数。内容涵盖参数累积、递归调用及箭头函数的精简技巧,帮助开发者深入理解柯里化的核心机制。

文章目录

函数柯里化的概念与类比

首先,我们需要明确柯里化的作用。通过观察以下示例函数,可以初步理解其基本形态。


let store = (a,b,c) => "这是你的七仔面" 
//函数就好像一个小卖部,一碗七仔面要三张软妹币:a,b,c (五个参数)

那么,什么是函数柯里化?我们可以将其类比为购买面条的过程。


let curryStore = curry(store) //刚刚的小卖部被柯里化了

//最佳状况
curryStore(1,5,1)// 老板,刚好!不用找了,刚好七块钱。老板: "这是你的七仔面" 

//偶尔出现的情况
let boss = curryStore(5) //老板,这五块钱你先拿着,我找找有没有一块钱。老板:……
boss = boss(1) //有了老板,这一块钱你先拿着,我找找还有没有一块钱。。老板:……
boss(1) //哈哈,终于找到了,给!老板:"这是你的七仔面" 

curryStore(5)(1)(1) //等价于上诉情况

由此可见,函数柯里化的核心在于逐步收集参数,并延迟函数的实际执行。这类似于先分期付款,后交付商品的模式。

柯里化函数的基础实现

接下来,我们通过一个具体案例来练习。

当前的目标是实现一个名为 curry 的函数,使其具备以下行为:当提供的参数数量足够时,执行原函数;若参数不足,则返回一个新的柯里化函数。


let curryPlus = curry((a,b,c) => a+b+c) //这里给到一个有三个参数的函数

curryPlus(1)(2)(3) //返回 6
curryPlus(1)(2,3) //返回 6
curryPlus(1,2)(3) //返回 6

let x = curryPlus(1)(2) //喂,怎么才两个参数?返回一个curry函数(已经带了两个参数)
x(1) //返回 4
x(2) //返回 5

对于熟练掌握 JavaScript 的开发者而言,一行代码解决此问题并非难事,但为了清晰展示逻辑,我们暂不直接给出最终答案。

因此,我们首先从传统的实现方法开始讲解。

根据柯里化的使用场景,其原理是返回一个接收不确定数量参数的函数(例如,用户可能一次性提供多个参数)。因此,我们可以利用不定参数语法来实现:


const curry = (fn) => {
  return (...args) =>{ //不定参数,想给多少给多少
    //给钱交货环节
  }
}

柯里化函数需要记录已传入的参数。如果尚未传入参数,则默认使用一个空数组:


const curry = (fn,arr=[]) => { //arr数组用于记录已有参数
  return (...args) =>{  
    //给钱交货环节
  }
}

在每次调用时,需检查参数总数是否已满足原函数的要求。若满足,则执行原函数 fn;若不满足,则返回一个新的柯里化函数,并将当前已收集的参数传入其中:


const curry = ( fn, arr = []) => {
  return (...args) => { 

    //判断参数总数是否和fn参数个数相等
    if([...arr, ...args].length === fn.length){
      return fn(...arr, ...args) //拓展参数,调用fn
    }else{
      return curry(fn,[...arr, ...args]) //迭代,传入现有的所有参数
    }

  }
}

至此,我们已经成功实现了基础的 curry 函数。

ES6一行代码的推导与优化

接下来,我们探讨如何使代码更加简洁。首先,可以将中间逻辑封装为立即执行函数,从而省略部分变量声明(如 ...arr 和 ...args):


const curry = ( fn, arr = []) => {
  return (...args) => { 

    return ( a => {  //a是一个数组
      if(a.length === fn.length) {
        return fn(...a)
      }else{
        return curry(fn, a)
      }
    })([...arr, ...args]) //这里把arr和args摊开成一个数组赋值给a

  }
}

此外,if 语句可以简化为三元表达式,进一步减少代码量:


const curry = ( fn, arr = []) => {
  return (...args) => { 

    return ( a => { 

      return a.length === fn.length ? fn(...a) : curry(fn, a)

    })([...arr, ...args]) 

  }
}

最后,既然函数体仅包含 return 语句,我们可以利用箭头函数的简洁语法(input => output),省略 return 关键字和大括号:


const curry = ( fn, arr = []) => {
  return (...args) => { 
    return ( a => a.length === fn.length? fn(...a) : curry(fn, a))([...arr, ...args])  //先折叠一层
  }
}

继续对代码进行折叠处理:


const curry = ( fn, arr = []) => {
  return (...args) => ( a => a.length === fn.length? fn(...a) : curry(fn, a))([...arr, ...args])  //再折叠
}

最终版本如下:


const curry = ( fn, arr = []) => (...args) => ( a => a.length === fn.length? fn(...a) : curry(fn, a))([...arr, ...args]) 
//衣服给您叠好了

你可以尝试运行以下代码进行验证:


const curry = ( fn, arr = []) => (...args) => ( a => a.length === fn.length? fn(...a) : curry(fn, a))([...arr, ...args])
let curryPlus = curry((a,b,c,d)=>a+b+c+d)

curryPlus(1,2,3)(4) //返回10
curryPlus(1,2)(4)(3) //返回10
curryPlus(1,2)(3,4) //返回10

特殊场景:参数个数为0时的处理

当然,柯里化函数的主要用途在于延迟执行。触发执行的條件不一定是参数数量相等,也可以是其他条件。例如,当参数个数为 0 时执行,这时我们需要对上述 curry 函数稍作修改:


const curry = ( fn, arr = []) => (...args) => ( (a,b) => b.length === 0? fn(...a) : curry(fn, a))([...arr, ...args],[...args])
let curryPlus = curry((...x)=>x.reduce((a,b)=>a+b))

curryPlus(1) //返回一个函数
curryPlus(1)(2) //返回一个函数

//遇到参数个数为0的情况才执行
curryPlus(1)(2)(4)() //返回7
curryPlus(1,2)(4)() //返回7

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章