簡單理解JavaScript中的柯里化和反柯里化
前言
本文旨在讓大家簡單理解柯里化和反柯里化,這里不做深入探究,只求能帶大家裝逼就好,看完還不懂你砍我。
我們先來簡單了解一下他們的作用。
柯里化又稱部分求值,字面意思就是不會立刻求值,而是到了需要的時候再去求值。如果看的懵逼,沒事,看完整篇文章再回過頭來看這里你就會豁然開朗。
反柯里化的作用是,當我們調(diào)用某個方法,不用考慮這個對象在被設(shè)計時,是否擁有這個方法,只要這個方法適用于它,我們就可以對這個對象使用它。
柯里化(curring)
我們有這樣一個場景,記錄程序員一個月的加班總時間,那么好,我們首先要做的是記錄程序員每天加班的時間,然后把一個月中每天的加班的時間相加,就得到了一個月的加班總時間。
但問題來了,我們有很多種方法可以實現(xiàn)它,比如最簡單的:
var monthTime = 0;
function overtime(time) {
return monthTime += time;
}
overtime(3.5); // 第一天
overtime(4.5); // 第二天
overtime(2.1); // 第三天
//...
console.log(monthTime); // 10.1
每次傳入加班時間都進行累加,這樣當然沒問題,但你知道,如果數(shù)據(jù)量很大的情況下,這樣會大大犧牲性能。
那怎么辦?這就是柯里化要解決的問題。
其實我們不必每天都計算加班時間,只需要保存好每天的加班時間,在月底時計算這個月總共的加班時間,所以,其實只需要在月底計算一次就行。
下面的overtime函數(shù)還不是一個柯里化函數(shù)的完整實現(xiàn),但可以幫助我們了解其核心思想:
var overtime = (function() {
var args = [];
return function() {
if(arguments.length === 0) {
var time = 0;
for (var i = 0, l = args.length; i < l; i++) {
time += args[i];
}
return time;
}else {
[].push.apply(args, arguments);
}
}
})();
overtime(3.5); // 第一天
overtime(4.5); // 第二天
overtime(2.1); // 第三天
//...
console.log( overtime() ); // 10.1
柯里化的核心思想就是這樣,看到這里你肯定已經(jīng)懂了,至于真正的柯里化函數(shù),網(wǎng)上有很多,大家可以去Google一下。
反柯里化(uncurring)
反柯里化的的作用已經(jīng)在前言說過了,這里講下它的由來。
2011年JavaScript之父Brendan Eich發(fā)表了一篇Twitter,提出了反柯里化這個思想,下面這段代碼是反柯里化的實現(xiàn)方式之一:
Function.prototype.uncurring = function() {
var self = this;
return function() {
var obj = Array.prototype.shift.call(arguments);
return self.apply(obj, arguments);
};
};
我們先來看看上面這段代碼有什么作用。
我們要把Array.prototype.push方法轉(zhuǎn)換成一個通用的push函數(shù),只需要這樣做:
var push = Array.prototype.push.uncurring();
//測試一下
(function() {
push(arguments, 4);
console.log(arguments); //[1, 2, 3, 4]
})(1, 2, 3)
arguments本來是沒有push方法的,通常,我們都需要用Array.prototype.push.call來實現(xiàn)push方法,但現(xiàn)在,直接調(diào)用push函數(shù),既簡潔又意圖明了。
就和前言寫的那樣,我們不用考慮對象是否擁有這個方法,只要它適用于這個方法,那就可以使用這個方法(類似于鴨子類型)。
我們來分析一下調(diào)用Array.prototype.push.uncurring()這句代碼時,發(fā)生了什么事情:
Function.prototype.uncurring = function() {
var self = this; //self此時是Array.prototype.push
return function() {
var obj = Array.prototype.shift.call(arguments);
//obj 是{
// "length": 1,
// "0": 1
//}
//arguments的第一個對象被截去(也就是調(diào)用push方法的對象),剩下[2]
return self.apply(obj, arguments);
//相當于Array.prototype.push.apply(obj, 2);
};
};
//測試一下
var push = Array.prototype.push.uncurring();
var obj = {
"length": 1,
"0" : 1
};
push(obj, 2);
console.log( obj ); //{0: 1,1: 2, length: 2 }
看到這里你應(yīng)該對柯里化和反柯里化有了一個初步的認識了,但要熟練的運用在開發(fā)中,還需要我們更深入的去了解它們內(nèi)在的含義。
Date: 2017-02-17