摘要:柯里化函數(shù)的作用函數(shù)柯里化允許和鼓勵你分隔復(fù)雜功能變成更小更容易分析的部分。指向的是調(diào)用方法的一個函數(shù),綁定,延遲執(zhí)行可見,之后返回的是一個延遲執(zhí)行的新函數(shù)關(guān)于性能的備注通常,使用柯里化會有一些開銷。
引子
有這樣一道題目,實(shí)現(xiàn)一個函數(shù),實(shí)現(xiàn)如下功能:
</>復(fù)制代碼
var result = sum(1)(2)(3);
console.log(result);//6
這道題目,印象中是一道技術(shù)筆試題。結(jié)合查到的資料,在這里做一下簡單的分析和總結(jié)。
一個簡單的例子題目給的還是比較寬的,沒多少限制,給了很多自由發(fā)揮的空間。
下面我們就一步一步的去實(shí)現(xiàn),一種簡單的做法可以是這樣的:
</>復(fù)制代碼
function add(a){
var sum = 0;
sum += a;
return function(b){
sum += b;
return function(c){
sum += c;
return sum;
}
}
}
add(1)(2)(3);//6
嗯,沒什么問題。
在此基礎(chǔ)上,我們再進(jìn)一步:
</>復(fù)制代碼
如果對調(diào)用的次數(shù)不加限制,比如 四次,那上面的代碼就不行了。
那該怎么辦呢?
觀察一下,我們可以發(fā)現(xiàn)返回的每一個函數(shù)執(zhí)行的邏輯其實(shí)都是一樣的。
就此我們可以精簡下代碼,讓函數(shù)返回后返回自身。
來試一下:
</>復(fù)制代碼
function add(a){
var sum = 0;
sum += a;
return function temp(b) {
sum += b;
return temp;
}
}
add(2)(3)(4)(5);
輸出的結(jié)果:
//function temp(b) {
// sum += b;
// return temp;
// }
并沒有像我們預(yù)期的那樣輸出 14,其實(shí)是這樣的,每次函數(shù)調(diào)用后返回的就是一個函數(shù)對象,那最后的結(jié)果,肯定是一個字符串表示啊。
要修正的話,有兩個辦法。
判斷參數(shù),當(dāng)沒有輸入?yún)?shù)時,返回調(diào)用結(jié)果:
</>復(fù)制代碼
function add(a){
var sum = 0;
sum += a;
return function temp(b) {
if (arguments.length === 0) {
return sum;
} else {
sum= sum+ b;
return temp;
}
}
}
add(2)(3)(4)(5)(); //14
如果要使用匿名函數(shù),也可以:
</>復(fù)制代碼
function add() {
var _args = [];
return function(){
if(arguments.length === 0) {
return _args.reduce(function(a,b) {
return a + b;
});
}
[].push.apply(_args, [].slice.call(arguments));
return arguments.callee;
}
}
var sum = add();
sum(2,3)(4)(5)(); //14
2 . 利用JS中對象到原始值的轉(zhuǎn)換規(guī)則。
</>復(fù)制代碼
當(dāng)一個對象轉(zhuǎn)換成原始值時,先查看對象是否有valueOf方法。
如果有并且返回值是一個原始值,那么直接返回這個值。
如果沒有valueOf 或 返回的不是原始值,那么調(diào)用toString方法,返回字符串表示。
那我們就為函數(shù)對象添加一個valueOf方法 和 toString方法:
</>復(fù)制代碼
function add(a) {
var sum = 0;
sum += a;
var temp = function(b) {
if(arguments.length===0){
return sum;
} else {
sum = sum+ b;
return temp;
}
}
temp.toString = temp.valueOf = function() {
return sum;
}
return temp;
}
add(2)(3)(4)(5); //14
寫到這里,我們來簡單總結(jié)下。
柯里化的定義一個通用的柯里化函數(shù)</>復(fù)制代碼
柯里化通常也稱部分求值,其含義是給函數(shù)分步傳遞參數(shù),每次傳遞參數(shù)后,部分應(yīng)用參數(shù),并返回一個更具體的函數(shù)接受剩下的參數(shù),中間可嵌套多層這樣的接受部分參數(shù)函數(shù),逐步縮小函數(shù)的適用范圍,逐步求解,直至返回最后結(jié)果。
</>復(fù)制代碼
var curring = function(fn){
var _args = [];
return function cb(){
if(arguments.length === 0) {
return fn.apply(this, _args);
}
Array.prototype.push.apply(_args, [].slice.call(arguments));
return cb;
}
}
var multi = function(){
var total = 0;
var argsArray = Array.prototype.slice.call(arguments);
argsArray.forEach(function(item){
total += item;
})
return total
};
var calc = curring(multi);
calc(1,2)(3)(4,5,6);
console.log(calc()); //空白調(diào)用時才真正計(jì)算
這樣 calc = currying(multi),調(diào)用非常清晰.
如果要 累加多個值,可以把多個值作為做個參數(shù) calc(1,2,3),也可以支持鏈?zhǔn)降恼{(diào)用,如 calc(1)(2)(3);
到這里, 不難看出,柯里化函數(shù)具有以下特點(diǎn):
函數(shù)可以作為參數(shù)傳遞
函數(shù)能夠作為函數(shù)的返回值
閉包
說了這么多,柯里化函數(shù)到底能夠幫我做什么,或者說,我為什么要用柯里化函數(shù)呢? 我們接著往下看。
柯里化函數(shù)的作用</>復(fù)制代碼
函數(shù)柯里化允許和鼓勵你分隔復(fù)雜功能變成更小更容易分析的部分。這些小的邏輯單元顯然是更容易理解和測試的,然后你的應(yīng)用就會變成干凈而整潔的組合,由一些小單元組成的組合。
</>復(fù)制代碼
1.提高通用性
</>復(fù)制代碼
function square(i) {
return i * i;
}
function double(i) {
return i *= 2;
}
function map(handeler, list) {
return list.map(handeler);
}
// 數(shù)組的每一項(xiàng)平方
map(square, [1, 2, 3, 4, 5]);
map(square, [6, 7, 8, 9, 10]);
map(square, [10, 20, 30, 40, 50]);
// ......
// 數(shù)組的每一項(xiàng)加倍
map(double, [1, 2, 3, 4, 5]);
map(double, [6, 7, 8, 9, 10]);
map(double, [10, 20, 30, 40, 50]);
例子中,創(chuàng)建了一個map通用函數(shù),用于適應(yīng)不同的應(yīng)用場景。顯然,通用性不用懷疑。同時,例子中重復(fù)傳入了相同的處理函數(shù):square和dubble。
應(yīng)用中這種可能會更多。當(dāng)然,通用性的增強(qiáng)必然帶來適用性的減弱。但是,我們依然可以在中間找到一種平衡。
看下面,我們利用柯里化改造一下:
</>復(fù)制代碼
function currying(fn) {
var slice = Array.prototype.slice,
__args = slice.call(arguments, 1);
return function () {
var __inargs = slice.call(arguments);
return fn.apply(null, __args.concat(__inargs));
};
}
function square(i) {
return i * i;
}
function double(i) {
return i *= 2;
}
function map(handeler, list) {
return list.map(handeler);
}
var mapSQ = currying(map, square);
mapSQ([1, 2, 3, 4, 5]); //[1, 4, 9, 16, 25]
var mapDB = currying(map, double);
mapDB([1, 2, 3, 4, 5]); //[2, 4, 6, 8, 10]
我們縮小了函數(shù)的適用范圍,但同時提高函數(shù)的適性.
2 延遲執(zhí)行。
</>復(fù)制代碼
柯里化的另一個應(yīng)用場景是延遲執(zhí)行。不斷的柯里化,累積傳入的參數(shù),最后執(zhí)行。
3.固定易變因素。
</>復(fù)制代碼
柯里化特性決定了它這應(yīng)用場景。提前把易變因素,傳參固定下來,生成一個更明確的應(yīng)用函數(shù)。最典型的代表應(yīng)用,是bind函數(shù)用以固定this這個易變對象。
</>復(fù)制代碼
Function.prototype.bind = function(ctx) {
var fn = this;
return function() {
fn.apply(ctx, arguments);
};
};
Function.prototype.bind 方法也是柯里化應(yīng)用與 call/apply 方法直接執(zhí)行不同,bind 方法 將第一個參數(shù)設(shè)置為函數(shù)執(zhí)行的上下文,其他參數(shù)依次傳遞給調(diào)用方法(函數(shù)的主體本身不執(zhí)行,可以看成是延遲執(zhí)行),并動態(tài)創(chuàng)建返回一個新的函數(shù), 這符合柯里化特點(diǎn)。
</>復(fù)制代碼
var foo = {
x: 666
};
var bar = function () {
console.log(this.x);
}.bind(foo); // 綁定
bar(); //666
// 下面是一個 bind 函數(shù)的模擬,testBind 創(chuàng)建并返回新的函數(shù),在新的函數(shù)中將真正要執(zhí)行業(yè)務(wù)的函數(shù)綁定到實(shí)參傳入的上下文,延遲執(zhí)行了。
Function.prototype.testBind = function (scope) {
var self = this; // this 指向的是調(diào)用 testBind 方法的一個函數(shù),
return function () {
return self.apply(scope);
}
};
var testBindBar = bar.testBind(foo); // 綁定 foo,延遲執(zhí)行
console.log(testBindBar); // Function (可見,bind之后返回的是一個延遲執(zhí)行的新函數(shù))
testBindBar(); // 666
關(guān)于curry性能的備注
</>復(fù)制代碼
通常,使用柯里化會有一些開銷。取決于你正在做的是什么,可能會或不會,以明顯的方式影響你。也就是說,幾乎大多數(shù)情況,你的代碼的擁有性能瓶頸首先來自其他原因,而不是這個。
有關(guān)性能,這里有一些事情必須牢記于心:
存取arguments對象通常要比存取命名參數(shù)要慢一點(diǎn).
一些老版本的瀏覽器在arguments.length的實(shí)現(xiàn)上是相當(dāng)慢的.
創(chuàng)建大量嵌套作用域和閉包函數(shù)會帶來花銷,無論是在內(nèi)存還是速度上.
以上 ;)
參考資料http://blog.jobbole.com/77956/
http://www.cnblogs.com/pigtai...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/81424.html
摘要:如果你對函數(shù)式編程有一定了解,函數(shù)柯里化是不可或缺的,利用函數(shù)柯里化,可以在開發(fā)中非常優(yōu)雅的處理復(fù)雜邏輯。同樣先看簡單版本的方法,以方法為例,代碼來自高級程序設(shè)計(jì)加強(qiáng)版實(shí)現(xiàn)上面函數(shù),可以換成任何其他函數(shù),經(jīng)過函數(shù)處理,都可以轉(zhuǎn)成柯里化函數(shù)。 我們經(jīng)常說在Javascript語言中,函數(shù)是一等公民,它們本質(zhì)上是十分簡單和過程化的。可以利用函數(shù),進(jìn)行一些簡單的數(shù)據(jù)處理,return 結(jié)果,...
摘要:如果有一方是布爾值,則轉(zhuǎn)換為,轉(zhuǎn)換為,再進(jìn)行判斷。等同運(yùn)算符類型不同返回類型相同如果同為數(shù)字字符串則比較值如果同為布爾值,相同則為不同為如果兩個操作數(shù)同為引用類型,且引用的為同一個對象函數(shù),數(shù)組,則相同。 本文主要記錄平時開發(fā)遇到的知識點(diǎn)和小技巧 相等判斷(==) 類型相同: 判斷其值是否相同 類型不同: 1. 如果數(shù)字和字符串比較, 則字符串會被隱式轉(zhuǎn)換為數(shù)字,在做判斷。 2....
摘要:柯里化通用式上面的柯里化函數(shù)沒涉及到高階函數(shù),也不具備通用性,無法轉(zhuǎn)換形參個數(shù)任意或未知的函數(shù),我們接下來封裝一個通用的柯里化轉(zhuǎn)換函數(shù),可以將任意函數(shù)轉(zhuǎn)換成柯里化。 showImg(https://segmentfault.com/img/remote/1460000018998373); 閱讀原文 前言 在 JavaScript 中,柯里化和反柯里化是高階函數(shù)的一種應(yīng)用,在這之前...
摘要:函數(shù)柯里化是把支持多個參數(shù)的函數(shù)變成接收單一參數(shù)的函數(shù),并返回一個函數(shù)能接收處理剩余參數(shù),而反柯里化就是把參數(shù)全部釋放出來。但在一些復(fù)雜的業(yè)務(wù)邏輯封裝中,函數(shù)柯里化能夠?yàn)槲覀兲峁└玫膽?yīng)對方案,讓我們的函數(shù)更具自由度和靈活性。 showImg(https://segmentfault.com/img/bVburN1?w=800&h=600); 柯里化(Curring, 以邏輯學(xué)家Has...
摘要:作為函數(shù)式編程語言,帶來了很多語言上的有趣特性,比如柯里化和反柯里化。在一些函數(shù)式編程語言中,會定義一個特殊的占位變量。個人理解不知道對不對延遲執(zhí)行柯里化的另一個應(yīng)用場景是延遲執(zhí)行。不斷的柯里化,累積傳入的參數(shù),最后執(zhí)行。作為函數(shù)式編程語言,JS帶來了很多語言上的有趣特性,比如柯里化和反柯里化。 這里可以對照另外一篇介紹 JS 反柯里化 的文章一起看~ 1. 簡介 柯里化(Currying)...
閱讀 9063·2021-11-18 10:02
閱讀 2609·2019-08-30 15:43
閱讀 2665·2019-08-30 13:50
閱讀 1385·2019-08-30 11:20
閱讀 2713·2019-08-29 15:03
閱讀 3634·2019-08-29 12:36
閱讀 935·2019-08-23 17:04
閱讀 625·2019-08-23 14:18