首頁  >  文章  >  web前端  >  詳解JavaScript函數柯里化

詳解JavaScript函數柯里化

黄舟
黄舟原創
2017-02-27 14:29:141505瀏覽

詳解JavaScript函數柯里化

百度百科對柯里化的解釋:在電腦科學中,柯里化(Currying)是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,並且傳回接受餘下的參數且傳回結果的新函數的技術。這項技術由 Christopher Strachey 以邏輯學家 Haskell Curry 命名的,儘管它是 Moses Schnfinkel 和 Gottlob Frege 發明的。
相信沒有幾個人在看了上面這段解釋之後能立刻理解函數柯里化是什麼東西。通俗一點講,函數柯里化的主要目的就是為了減少函數傳參,同時將一些固定參數私有化。下面展示一段非常簡單計算圓面積的程式碼來說明函數柯里化的原理:

//circle函数,接受半径r和πfunction circle(r,p){
    //计算半径为r的圆的面积
    var area=p*r*r;    return area;
}/*
 * 通过函数柯里化来简化circle函数,只传入半径就能计算出面积
 * 不管怎么样,π是不会变的,因此我们将他写死,不需要外部调用传入
 */function curryCircle(r){
    var p=3.14;    var area=p*r*r;    return area;
}

也許你會覺得這段程式碼很二,但這就是函數柯里化的真實面。當然上面的程式碼只是一個非常小的例子,真實世界中的函數柯里化會比它兇惡一點,下面來討論一個更通用的例子。假設π不是唯一的(例如我們有三種π),我們計算圓面積公式當中的π會根據場景不同而變化,這時候我們就不能直接寫死,而需要根據不同環境來配置π:

//circle函数,接受半径r和π
function circle(r,p){
    //计算半径为r的圆的面积
    var area=p*r*r;    
    return area;
}
//针对circle函数的柯里化函数function curry(fn,p){
    var finalMethod=function(r){
        var result=fn(r,p);        return result;
    }    return finalMethod;
}
//我们有3种不同的πvar curryCircle1=curry(circle,1.14);
var curryCircle2=curry(circle,2.14);
var curryCircle3=curry(circle,3.14);
//输出:4.56  8.56  12.56
console.log(curryCircle1(2),curryCircle2(2),curryCircle3(2));

可以看到,curry方法透過封裝最基礎的circle方法,同時保存設定好的p參數(π),並傳回一個finalMethod方法,這樣我們最終呼叫finalMethod時就只需要傳入參數r(半徑)就可以完成。借助函數柯里化,我們擁有了三種簡化的計算圓面積方法。上面所示的函數柯里化只能適用於圓面積的計算,這次我們寫一個更通用的柯里化函數:

function curry(fn){
    //第一个参数是基础执行方法,slice切除
    var args=Array.prototype.slice.call(arguments,1);    //直接返回匿名函数
    return function(){
        //slice新参数以便能调用concat
        var innerArgs=Array.prototype.slice.call(arguments);        //将配置的参数和新传入的参数合并
        var finalArgs=args.concat(innerArgs);        return fn.apply(null,finalArgs);
    };
}

curry()函數的主要工作就是將被傳回函數的參數進行排序。 Curry()的第一個參數是要進行柯里化的函數,其他參數是要傳入的值。為了取得第一個參數之後的所有參數,在arguments物件上呼叫了slice()方法,並傳入參數1表示被傳回的陣列包含從第二個參數開始的所有參數。然後args數組包含了來自外部函數的參數。在內部函數中,創建了innerArgs數組用來存放所有傳入的參數(又一次使用了slice())。有了存放來自外部函數和內部函數的參數數組後,就可以使用concat()方法將他們合併成finalArgs。最後使用apply()將結果傳遞給函數。這樣就實現了一個通用的函數柯里化。如果到這此還有餘力的讀者可以接著往下看,我們將要介紹jQuery中所使用的函數柯里化。
在針對某個方法進行柯里化時,我們甚至不用傳入fn來告訴柯里化來包裝我們的函數,我們可以透過原型直接將函數和柯里化綁定:

Function.prototype.curry=function(){
    //利用原型的便利,我们可以直接通过this引用到方法
    var fn=this;    var args=Array.prototype.slice.call(arguments);    return function(){
        var arg=0;        //循环校验先前传入的参数和新传入的参数是否有差别
        for(var i=0;i<args.length && arg<arguments.length;i++){            if(args[i]===undefined){
                args[i]=arguments[arg++];
            }
        }        return fn.apply(this,args);
    };
};

與之前不同,我們透過this引用獲取了方法引用,這樣當我們需要將某個函數柯里化時,只要這樣寫就可以:

var delay=setTimeout.curry(undefined,10);

delay就是一個已經被提前設定了10毫秒延遲的setTimeout函數。我們仍然透過args來保存參數配置,不過這次有點差別:在for迴圈內部會校驗args和arguments的差別,以此判斷來完成參數拼接。所以傳給curry的參數必須是完整參數(即意味著不傳的值要傳入undefined)。最後我們實作了一個不需要傳入fn的柯里化方法。

詳解JavaScript函數柯里化

百度百科對柯里化的解釋:在電腦科學中,柯里化(Currying)是把接受多個參數的函數轉換成接受一個單一參數(原函數的第一個參數)的函數,並且傳回接受餘下的參數且傳回結果的新函數的技術。這項技術由 Christopher Strachey 以邏輯學家 Haskell Curry 命名的,儘管它是 Moses Schnfinkel 和 Gottlob Frege 發明的。
相信沒有幾個人在看了上面這段解釋之後能立刻理解函數柯里化是什麼東西。通俗一點講,函數柯里化的主要目的就是為了減少函數傳參,同時將一些固定參數私有化。下面展示一段非常簡單計算圓面積的程式碼來說明函數柯里化的原理:

//circle函数,接受半径r和πfunction circle(r,p){
    //计算半径为r的圆的面积
    var area=p*r*r;    return area;
}/*
 * 通过函数柯里化来简化circle函数,只传入半径就能计算出面积
 * 不管怎么样,π是不会变的,因此我们将他写死,不需要外部调用传入
 */function curryCircle(r){
    var p=3.14;    var area=p*r*r;    return area;
}

也許你會覺得這段程式碼很二,但這就是函數柯里化的真實面。當然上面的程式碼只是一個非常小的例子,真實世界中的函數柯里化會比它兇惡一點,下面來討論一個更通用的例子。假設π不是唯一的(例如我們有三種π),我們計算圓面積公式當中的π會根據場景不同而變化,這時候我們就不能直接寫死,而需要根據不同環境來配置π:

//circle函数,接受半径r和πfunction circle(r,p){
    //计算半径为r的圆的面积
    var area=p*r*r;    return area;
}//针对circle函数的柯里化函数function curry(fn,p){
    var finalMethod=function(r){
        var result=fn(r,p);        return result;
    }    return finalMethod;
}
//我们有3种不同的π
var curryCircle1=curry(circle,1.14);
var curryCircle2=curry(circle,2.14);
var curryCircle3=curry(circle,3.14);
//输出:4.56  8.56  12.56
console.log(curryCircle1(2),curryCircle2(2),curryCircle3(2));

可以看到,curry方法通过封装最基础的circle方法,同时保存设置好的p参数(π),并返回一个finalMethod方法,这样我们最终调用finalMethod时就只需要传入参数r(半径)就可以完成。借助函数柯里化,我们拥有了三个简化的计算圆面积方法。上面展示的函数柯里化只能适用于圆面积的计算,这次我们编写一个更通用的柯里化函数:

function curry(fn){
    //第一个参数是基础执行方法,slice切除
    var args=Array.prototype.slice.call(arguments,1);    //直接返回匿名函数
    return function(){
        //slice新参数以便能调用concat
        var innerArgs=Array.prototype.slice.call(arguments);        //将配置的参数和新传入的参数合并
        var finalArgs=args.concat(innerArgs);        return fn.apply(null,finalArgs);
    };
}

curry()函数的主要工作就是将被返回函数的参数进行排序。Curry()的第一个参数是要进行柯里化的函数,其他参数是要传入的值。为了获取第一个参数之后的所有参数,在arguments对象上调用了slice()方法,并传入参数1表示被返回的数组包含从第二个参数开始的所有参数。然后args数组包含了来自外部函数的参数。在内部函数中,创建了innerArgs数组用来存放所有传入的参数(又一次使用了slice())。有了存放来自外部函数和内部函数的参数数组后,就可以使用concat()方法将他们合并成finalArgs。最后使用apply()将结果传递给该函数。这样就实现了一个通用的函数柯里化。如果到这此还有余力的读者可以接着往下看,我们将要介绍jQuery中使用的函数柯里化。
在针对某个方法进行柯里化时,我们甚至不用传入fn来告诉柯里化来包装我们的函数,我们可以通过原型直接将函数和柯里化绑定:

Function.prototype.curry=function(){
    //利用原型的便利,我们可以直接通过this引用到方法
    var fn=this;    var args=Array.prototype.slice.call(arguments);    return function(){
        var arg=0;        //循环校验先前传入的参数和新传入的参数是否有差别
        for(var i=0;i<args.length && arg<arguments.length;i++){            if(args[i]===undefined){
                args[i]=arguments[arg++];
            }
        }        return fn.apply(this,args);
    };
};

与之前不同,我们通过this引用获取了方法引用,这样当我们需要将某个函数柯里化时,只要这样写就可以:

var delay=setTimeout.curry(undefined,10);

delay就是一个已经被提前设定了10毫秒延迟的setTimeout函数。我们仍然通过args来保存参数配置,不过这次有点区别:在for循环内部会校验args和arguments的区别,以此判断来完成参数拼接。所以传给curry的参数必须是完整参数(即意味着不传的值要传入undefined)。最终我们实现了一个不需要传入fn的柯里化方法。

 以上就是详解JavaScript函数柯里化 的内容,更多相关内容请关注PHP中文网(www.php.cn)!


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn