一開始看到這個題目我最先想到了閉包,
可能會這么寫:
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
參考:
https://juejin.im/post/5e6ed0bc6fb9a07c8334f75c
https://juejin.im/post/5e40b566e51d4526ea7ee623