一开始看到这个题目我最先想到了闭包,
可能会这么写:
function sum(a){ return function(b){ return function(c){ return function(d){ ...... } } } }
或许也会这么写:
let sum = a => b => c => d => ... => a+b+c+d+...+n
但是不论是以上哪种方式,都需要先固定参数个数,因此这两种写法都不可取
解决办法——递归调用
方法一:使用toString打印
思路:当我们直接对函数使用 alert() 或 console.log() 时,函数的 toString() 方法会被调用。注意,valueOf方法会把数据类型转换成原始类型,toString方法会把数据类型转换成string类型,如果是对象会返回,toString() 返回 “[object type]”,其中type是对象类型。正常情况下,优先调用toString()。有运算操作符的情况下,valueOf()的优先级高于toString(),当调用valueOf()方法无法运算后还是会再调用toString()方法
代码:
function sum(a){ let temp = function(b){ return sum(a+b) } // temp.toString这里写成temp.valueOf也可以 temp.toString = function(){ return a } return temp } let ans = sum(1)(2)(3) console.log(ans)
执行sum(1),此时a=1,返回temp函数
②执行temp(2),这个函数内执行sum(a+b),即sum(a+b)=sum(1+2)=sum(3),此时a=3,并且返回temp函数
③执行temp(3),这个函数内执行sum(a+b),即sum(a+b)=sum(3+3)=sum(6),此时m=6,并且返回temp函数
④后面没有传入参数,等于返回的temp函数不被执行而是打印。代码中的temp.toString的重写只是为了函数不执行时能够返回最后运算的结果值,这里即为6
方法二:函数柯里化
思路:也是用到toString打印,但是这里用到了函数式编程的思想,这里的sum(1)(2)(3)(4)...(n)等价于sum(1)(2,3)(4)...(n),也等价于sum(1,2,3)(4)...(n)等多种排列组合
代码:
let sum = 0 function add (...args) { for(let i=0;i<args.length;i++){ sum = sum + args[i] } return sum } function currying (fn) { let val = null let temp = function(...newArgs) { val = fn.apply(this, newArgs) return temp } temp.toString = function(){ sum = 0 return val } return temp } let addCurry = currying(add) console.log(addCurry(1)(2)(3)(4, 5)) //15 这里是函数值为15,本质是函数字符串值 console.log(addCurry(1)(2)(3, 4, 5)) //15 console.log(addCurry(1, 2)(3, 4, 5)) //15
补充:
①函数柯里化就是只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。简单来说,就是每次调用函数时,它只接受一部分参数,并返回一个函数,直到传递所有参数为止
②toString返回的是函数字符串值,后期可以通过Number函数将函数字符串值转换为数值
扩展:求sum(1)(2)(3)...(n)()
方法一:
代码:
function sum(a){ return function(b){ if(b!==undefined){ return sum(a+b) }else{ return a } } } let ans = sum(1)(2)(3)() console.log(ans)
方法二——函数柯里化
代码:
function add (...args) { //求和 return args.reduce((a, b) => a + b) } function currying (fn) { let args = [] return function temp (...newArgs) { if (newArgs.length) { args = [ ...args, ...newArgs ] return temp } else { let val = fn.apply(this, args) args = [] //保证再次调用时清空 return val } } } let addCurry = currying(add) // 注意调用方式的变化 console.log(addCurry(1)(2)(3)(4, 5)()) //15 console.log(addCurry(1)(2)(3, 4, 5)()) //15 console.log(addCurry(1)(2, 3, 4, 5)()) //15
参考: