一开始看到这个题目我最先想到了闭包,
可能会这么写:
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
参考: