前端開發者進階之函數反柯里化unCurrying


函數柯里化,是固定部分參數,返回一個接受剩余參數的函數,也稱為部分計算函數,目的是為了縮小適用范圍,創建一個針對性更強的函數。

那么反柯里化函數,從字面講,意義和用法跟函數柯里化相比正好相反,擴大適用范圍,創建一個應用范圍更廣的函數。使本來只有特定對象才適用的方法,擴展到更多的對象。

看一下通用函數:

Function.prototype.currying = function() {
    var that = this;
    return function() {
        return Function.prototype.call.apply(that, arguments);
    }
}

短小精悍,科學上講,濃縮的都是精品,但越精品的往往越難以理解。分解一下:

1 為Function原型添加unCurrying方法,這樣所有的function都可以被借用;

2 返回一個借用其它方法的函數,這是目的;

3 借用call方法實現,但call方法參數傳入呢?借用apply,至此完畢。

回頭看看,好像也不難!

還有其它的實現方式:

Function.prototype.unCurrying = function () {
    var f = this;
    return function () {
        var a = arguments;
        return f.apply(a[0], [].slice.call(a, 1));
    };
};
Function.prototype.unCurrying = function () {
    return this.call.bind(this);
};

 原理都相同,最終是把this.method轉化成 method(this,arg1,arg2....)以實現方法借用和this的泛化。

鴨式辯型:如果一個對象可以像鴨子一樣走路,游泳,並且嘎嘎叫,就認為這個對象是鴨子,哪怕它並不是從鴨子對象繼承過來的。

在javascript里面,很多函數都不做對象的類型檢測,而是只關心這些對象能做什么。

Array構造器和String構造器的prototype上的方法就被特意設計成了鴨子類型。這些方法不對this的數據類型做任何校驗。這也就是為什么arguments能冒充array調用push方法.
看下v8引擎里面Array.prototype.push的代碼:
function ArrayPush() {
    var n = TO_UINT32(this.length);
    var m = %_ArgumentsLength();
    for (var i = 0; i < m; i++) {
        this[i + n] = %_Arguments(i); //屬性拷貝
        this.length = n + m; //修正length
        return this.length;
    }
}

這就給對象冒充創造了條件,也就我們討論的函數柯反里化unCurrying。反柯里化其實反映的是一種思想,擴大方法的適用范圍!

看下面例了,讓一個普通對象具備push方法:

var push = Array.prototype.push.unCurrying(),
obj = {};
push(obj, 'first', 'two');
console.log(obj);
/*obj {
    0 : "first",
    1 : "two"
}*/

obj被push了兩個元素:first 和two,同時還具備了一個length屬性,其實我們創建了一類數組對象。

 再看一個例子:

var toUpperCase = String.prototype.toUpperCase.unCurrying();
console.log(toUpperCase('avd')); // AVD

function AryUpper(ary) {
    return ary.map(toUpperCase);
}

console.log(AryUpper(['a', 'b', 'c'])); // ["A", "B", "C"]

只是方法都可以借用。包括call方法。

var call = Function.prototype.call.unCurrying();
function $(id) {
    return this.getElementById(id);
}
var demo = call($, document, 'demo');
console.log(demo);

這似乎看起來相對於前兩個例子,比較難理解,其實一句話就可以解釋:document借用了$方法,並替換了其中的this。

在函數柯里化例子中bind的例子中,柯里化的目的是為了固定可變參數this。而反柯里化,把原來擁有方法的this泛化了,泛化到所有對象都可以借用,也就是替代當前擁有方法的this。

這里,希望不要混淆,這里的this並不是指上例中的this,上例中的this只是因為借用的方法是call。

更有趣的是,unCurrying本身也是方法,它是否可以被借用呢?答案是肯定的。這就是js的奇妙之處,反柯里化的奇妙之處。

看下面:

var unCurrying = Function.prototype.unCurrying.unCurrying();
var map = unCurrying(Array.prototype.map);
var sq = map([1, 2, 3],
function(n) {
    return n * n;
});
console.log(sq); // [1,4,9]

無論是柯里化還是反柯里化,其實反應的都是一種設計思想。這一節先到這里。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM