【JS面试题】函数柯里化的三种方式

简介: 在面试中你是否在被问到函数柯里化而一头雾水?或是现在你是否看到函数柯里化而一脸懵逼?这到底是个什么东西?

函数柯里化的定义


函数柯里化(Currying)是一种高级函数的用法,其可以把接收多个参数的函数变成接收单个参数并且返回接受余下的参数而且返回结果的新函数


现在有一个 add 函数,其作用是返回所有参数的和,调用方法如下


    add(1, 2, 3, 4, 5)  // 返回 15


    现在我们要把该函数的调用方式变成另一种,如下列代码


      add(1)(2)(3)(4)(5)   // 返回 15add(1)(2, 3, 4)(5)   // 返回 15


      首先说明一下,参数的个数不是固定的,可以是1个,也可以是上千个


      看到这里,你能想到有什么办法能实现上述需求吗?这里我给出了三种实现方式,一起来看一下


      方法一


      先来看看第一种方式的完整代码


        function add() {    // 创建数组,用于存放之后接收的所有参数    let args = [...arguments]        // 设置一个递归调用的函数,每次调用将传进来的参数添加到args中,并返回该函数等待下一次调用    function getArgs() {        args.push(...arguments)        return getArgs    }    // 重新定义getArgs的toString函数,用于最后一次调用完该函数后展示所有参数之和    getArgs.toString = function() {        return args.reduce((a, b) => {            return a + b        })    }
            return getArgs}


        首先,我们根本不知道之后会传入多少个参数,因此我们无法确认递归函数何时结束,但我们却通过递归函数将求和结果延时了,即不管我们调用几次add函数,都不会返回求和结果,但此时如何让add函数知道我们已经传完参数了呢?


        这就要涉及一个非常冷门的知识了,我们来看一下


        先简单定义一个add函数,函数内将自身返回,代码如下


          function add() {    let str = '我是add函数'    return add}let res = add()console.log(res);console.log(typeof res)


          我们来看看在浏览器中,会打印什么结果,结果如下图


          112d50308fb7438881830deec5c94ed2.png


          可以看到,add函数返回的结果类型是function类型,但在浏览器中被隐式地转化为字符串了。


          其实这是调用了add函数的toString方法,将toString方法的返回值作为隐式转化后的结果才实现的,不信我们修改代码来实现一下


            function add() {    let str = '我是add函数'    return add}add.toString = function() {    return '我是add函数的toString方法'}let res = add()console.log(res);console.log(typeof res);


            然后再来看看浏览器中的打印结果


            24e4b5b251d346d2fe00fa1a22e7b052.png


            因此我们就可以利用该特性,将求和的代码写在add函数的toString方法中,那么在最后一次调用add函数后,返回的就是所有参数的和了


            缺点: 不过此方法也有所缺点,那就是返回的结果前面会有一个 f 表示其原本是函数类型的只是隐式转化成了字符串

            方法二


            第二种方法是通过判断有无传入参数来确认是否是最后一次调用,我们先来看看完整代码


              function add() {    // 先将第一次调用的所有参数存放在args中    let args = [...arguments]
                  return function() {        // 若没有传入参数,则直接将args中存放的所有值求和并返回        if(arguments.length == 0) {            return args.reduce((a, b) => {                return a + b            })        }         // 若有传入参数,则将参数存放到args中,并继续返回该函数接收下一次调用        else {            let _args = [...arguments]            for(let i = 0; i < _args.length; i++) {                args.push(_args[i])            }            return arguments.callee        }    }}


              这个方法实现的思路应该是很简单的,我就不做多余的讲解了,直接来看看如何使用的吧


                add(1)(2)(3)(4)(5)()  // 返回 15add(1)(2)(3)(4)(5)    // 返回了一个匿名函数


                缺点: 该方法的缺点就是要在最后再自调用一次,表示传参完毕,这样才能获得之前传入所有参数的和


                方法三


                第三种方法是在传参之前先设定自己需要传入的参数个数 n,然后函数内部会自动判断你总共传入的参数个数,当等于 n 时,则自动求和并返回,我们来看一下代码


                  function curry(length) {    // 第一个参数是剩余传入参数个数,因此不用存放在args中    let args = [...arguments].slice(1)
                      return function() {        // 将此次传入的参数与之前所有的参数合并        args = args.concat([...arguments])        // 可传入参数个数大于0        if(arguments.length < length) {            // 返回curry函数,并将第一个参数length减去传入参数的个数,表示剩余可穿参数个数            return curry.apply(this, [length - arguments.length].concat(args))        }         // 可传入参数个数为0        else {            // 求和并返回            return args.reduce((a, b) => a + b)        }    }}


                  该方法的思想很奇妙,就是在一次调用以后,判断剩余的可传入参数个数 length,若还能继续传入参数就重新调用一次 curry 函数,把 length 和 之前所有传过来的参数都作为其参数传入


                  来看一下使用方式吧


                    let add = curry(5)add(1)(2)(3)(4)(5)  // 返回 15


                    缺点:每次都要先设定传入参数的个数,不太灵活


                    END


                    我是Lpyexplore,一个前端的探索者。

                    相关文章
                    |
                    1月前
                    |
                    JavaScript 前端开发 Java
                    [JS]同事:这次就算了,下班回去赶紧补补内置函数,再犯肯定被主管骂
                    本文介绍了JavaScript中常用的函数和方法,包括通用函数、Global对象函数以及数组相关函数。详细列出了每个函数的参数、返回值及使用说明,并提供了示例代码。文章强调了函数的学习应结合源码和实践,适合JavaScript初学者和进阶开发者参考。
                    41 2
                    [JS]同事:这次就算了,下班回去赶紧补补内置函数,再犯肯定被主管骂
                    |
                    1月前
                    |
                    JSON JavaScript 前端开发
                    [JS]面试官:你的简历上写着熟悉jsonp,那你说说它的底层逻辑是怎样的?
                    本文介绍了JSONP的工作原理及其在解决跨域请求中的应用。首先解释了同源策略的概念,然后通过多个示例详细阐述了JSONP如何通过动态解释服务端返回的JavaScript脚本来实现跨域数据交互。文章还探讨了使用jQuery的`$.ajax`方法封装JSONP请求的方式,并提供了具体的代码示例。最后,通过一个更复杂的示例展示了如何处理JSON格式的响应数据。
                    34 2
                    [JS]面试官:你的简历上写着熟悉jsonp,那你说说它的底层逻辑是怎样的?
                    |
                    1月前
                    |
                    SQL Oracle 关系型数据库
                    [Oracle]面试官:你举例几个内置函数,并且说说如何使用内置函数作正则匹配
                    本文介绍了多种SQL内置函数,包括单行函数、非空判断函数、日期函数和正则表达式相关函数。每种函数都有详细的参数说明和使用示例,帮助读者更好地理解和应用这些函数。文章强调了字符串操作、数值处理、日期计算和正则表达式的使用方法,并提供了丰富的示例代码。作者建议读者通过自测来巩固学习成果。
                    20 1
                    [Oracle]面试官:你举例几个内置函数,并且说说如何使用内置函数作正则匹配
                    |
                    1月前
                    |
                    前端开发 JavaScript 开发者
                    除了 Generator 函数,还有哪些 JavaScript 异步编程解决方案?
                    【10月更文挑战第30天】开发者可以根据具体的项目情况选择合适的方式来处理异步操作,以实现高效、可读和易于维护的代码。
                    |
                    2月前
                    |
                    JavaScript 前端开发
                    JavaScript 函数语法
                    JavaScript 函数是使用 `function` 关键词定义的代码块,可在调用时执行特定任务。函数可以无参或带参,参数用于传递值并在函数内部使用。函数调用可在事件触发时进行,如用户点击按钮。JavaScript 对大小写敏感,函数名和关键词必须严格匹配。示例中展示了如何通过不同参数调用函数以生成不同的输出。
                    |
                    2月前
                    |
                    存储 JavaScript 前端开发
                    JS函数提升 变量提升
                    【10月更文挑战第6天】函数提升和变量提升是 JavaScript 语言的重要特性,但它们也可能带来一些困惑和潜在的问题。通过深入理解和掌握它们的原理和表现,开发者可以更好地编写和维护 JavaScript 代码,避免因不了解这些机制而导致的错误和不一致。同时,不断提高对执行上下文等相关概念的认识,将有助于提升对 JavaScript 语言的整体理解和运用能力。
                    |
                    2月前
                    |
                    JavaScript 前端开发
                    js教程——函数
                    js教程——函数
                    42 4
                    |
                    2月前
                    |
                    存储 JavaScript 前端开发
                    js中函数、方法、对象的区别
                    js中函数、方法、对象的区别
                    21 2
                    |
                    2月前
                    |
                    JavaScript 前端开发
                    Node.js 函数
                    10月更文挑战第5天
                    24 3
                    |
                    2月前
                    |
                    存储 JavaScript 前端开发
                    JavaScript数据类型全解:编写通用函数,精准判断各种数据类型
                    JavaScript数据类型全解:编写通用函数,精准判断各种数据类型
                    26 0