JS泛型函數

2020-07-16 10:05:05
JavaScript 具有動態型別語言的部分特點,如使用者不用關心一個物件是否擁有某個方法,一個物件也不限於只能使用自己的方法——使用 call 或 apply 動態呼叫,可以使用其他物件的方法。這樣該方法中的 this 就不再局限於原物件,而是被泛化,從而得到更廣泛的應用。

泛型函數(Uncurry)的設計目的是:將泛化 this 的過程提取出來,將 fn.call 或 fn.apply 抽象成通用的函數。
Function.prototype.uncurry = function () {  //泛型函數
    var self = this;
    return function () {
        return Function.prototype.apply.apply(self, arguments);
    }
};

下面將 Array.prototype.push 原型方法進行泛化,此時 push 函數的作用與 Array.prototype.push 一樣,但不局限於操作 Array 物件,還可以操作 Object 物件。
//泛化Array.prototype.push
var push = Array.prototype.push.uncurry();
var obj = {};
push(obj, [3,4,5]);  //可以把陣列轉換為類陣列
for (var i in obj) {
    console.log(i);  //輸出類陣列:{0 : 3, 1 : 4, 2 : 5, length : 3}
}

逆向解析

簡單逆向分析一下泛型函數的設計思路。

首先,呼叫 push(obj, [3,4,5]);,等效於以下原始動態呼叫的方法。
Array.prototype.push.apply(obj, [3,4,5]);
其次,呼叫 Array.prototype.push.uncurry(); 泛型化後,實際上 push() 就是以下函數。
push = function () {
    return Function.prototype.apply.apply(Array.prototype.push, arguments);
}
最後,呼叫 push(obj, [3,4,5]);,程式碼進行以下邏輯轉換。
Array.prototype.push.(Function.prototype.apply) (obj, [3,4,5]);
即為:
Array.prototype.push.apply(obj, [3,4,5]);
實際上,上面程式碼使用了兩個 apply 動態呼叫,實現邏輯思路的兩次轉換。