天天看點

(手寫)柯裡化 - toString 了解

在前面一篇文章我們提到:

柯裡化可以通過閉包+遞歸實作

let arr = []
function addCurry() {
   let arg = Array.prototype.slice.call(arguments); // 收集參數
   arr = arr.concat(arg);
    if (arg.length === 0) { // 如果參數為空,則判斷遞歸結束
        return arr.reduce((a,b)=>{return a+b}) // 求和
    } else {
        return addCurry;
    }
}

addCurry(1)(2)(3)() // 6      

這樣寫,沒什麼毛病,通過:​

​Array.prototype.slice.call(arguments)​

​​ 收集了參數,推到 ​

​arr​

​​ 數組裡;當存在參數時,遞歸調用,當沒有參數的時候,再用 ​

​reduce()​

​ 求和,傳回;

但是這樣寫,會有外部變量 arr,arr 不清理的話,會被一直儲存,導緻連續調用出錯:

addCurry(1)(2)(3)() // 6
addCurry(1)() // 7      

是以,需要改寫成這樣:

function addCurry() {
    let arr = [...arguments]
    let fn = function () {
        if(arguments.length === 0) {
        return arr.reduce((a, b) => a + b)
        } else {
            arr.push(...arguments)
            return fn
        }
    }
    return fn
}      

把 arr 再加一層封裝到函數裡,每次調用,都會重新聲明,原值會被清理;

之前相加的思路仍然不變;

但這樣做,還有一個問題:總是需要用空括号 () 為結尾,來判斷結束調用;

能不能去掉這個,直接如下這樣就能求值:

addCurry(1)(2)(3)

//或

addCurry(1)(2,3)      

答案是可以的,原理是用到 ​

​toString​

​ ,當用 Function 的值做計算的時候,會調用 toString 做隐式轉換;

let fn = function(){}

fn.toString = () => 1

fn == 1 // true      

是以,我們的代碼更新為:

function addCurry() {

    let arr = [...arguments]

    // 利用閉包的特性收集所有參數值
    var fn = function() {
        arr.push(...arguments);
        return fn;
    };

    // 利用 toString 隐式轉換
    fn.toString = function () {
        return arr.reduce(function (a, b) {
            return a + b;
        });
    }
    return fn;
}

addCurry(1)(2)(3) == 6 // true      

這裡一定要注意,直接列印 addCurry(1)(2)(3) 是不能拿值的,隻有當它做隐式轉化的時候,才能計算得正确得值。

小結:

其實不管是用空括号 () 作“開始執行reduce相加”的判斷依據,還是用 toString 的隐式轉換做依據,總是要有一個标準,來告訴柯裡化函數:你可以執行了!

柯裡化精髓是參數收集,是延遲執行!

繼續閱讀