什么是js函數的currying/柯里化?

2016-10-24 1468 2 編輯:深色多郎 來源:程序設計書籍

說到js的柯里化,相信很多朋友都會頭大。或者不是很清楚。我今天簡單的給大家介紹一下!我用一句話總結函數柯里化,js柯里化是逐步傳參,逐步縮小函數的適用范圍,逐步求解的過程。

可能對這句話你不是很清楚,那么,我們來看個案例,簡單說明一下:

需求:我們寫一個函數,將函數的幾個參數相加,返回結果!那我們寫的函數如下

var concat3Words = function (a, b, c) {

    return a+b+c;

};

函數柯里化呢?是分部求解,先傳一個a參數,再傳一個b參數,再傳一個c參數,最后將這三個參數相加!

var concat3WordsCurrying = function(a) {

    return function (b) {

        return function (c) {

            return a+b+c;

        };

    };

};

我們看輸出結果:

console.log(concat3Words("foo ","bar ","baza"));            // foo bar baza

console.log(concat3WordsCurrying("foo "));                  // [Function]

console.log(concat3WordsCurrying("foo ")("bar ")("baza"));  // foo bar baza 函數鏈式調用

柯里化案例

上面的需求我們稍微變一下。現在我們更進一步,如果要求可傳遞的參數不止3個,可以傳任意多個參數,當不傳參數時輸出結果?那現在我們用柯里化來簡單的實現一下:

var adder = function () {

    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 = adder();

console.log(sum);     // Function

sum(100,200)(300);    // 調用形式靈活,一次調用可輸入一個或者多個參數,并且支持鏈式調用

sum(400);

console.log(sum());   // 1000 (加總計算) 

上面 adder是柯里化了的函數,它返回一個新的函數,新的函數接收可分批次接受新的參數,延遲到最后一次計算。我們可以任意傳入參數,當不傳參數的時候,輸出計算結果!

基礎知識普及之arguments

看到上面的柯里化實現,可能有的朋友會有點暈,其實上面也沒有什么新的知識,假如你看過我之前寫的文章,相信能夠理解!apply,call之前都有介紹。前端干貨中的第四條,也有提及!唯獨arguments前面文章沒有介紹。

arguments對象是比較特別的一個對象,實際上是當前函數的一個內置屬性。arguments非常類似Array,但實際上又不是一個Array實例。

我們看一下下面的例子:

function f(a, b, c){

    alert(arguments.length);   // result: "2"

    a = 100;

    alert(arguments[0]);       // result: "100"

    arguments[0] = "haorooms";

    alert(a);                  // result: "haorooms"

    alert(c);                  // result: "undefined"

    c = 2016;

    alert(arguments[2]);       // result: "undefined"

}

f(1, 2);

我們通常用

[].slice.call(arguments, 1)

將傳入參數轉為數組。slice是js的一個函數,關于其用法,不會的去查一下!上面的寫法相當于Array.prototype.slice.call(arguments, 1);

另外,arguments對象中有一個非常有用的屬性:callee。arguments.callee返回此arguments對象所在的當前函數引用。在使用函數遞歸調用時推薦使用arguments.callee代替函數名本身。arguments就介紹到這里!

通用的柯里化函數

下面這個是通用的柯里化函數

var currying = function (fn) {

    var _args = [];

    return function () {

        if (arguments.length === 0) {

            return fn.apply(this, _args);

        }

        Array.prototype.push.apply(_args, [].slice.call(arguments));

        return arguments.callee;

    }

};

我們可以通過如下函數簡單應用一下柯里化:

var multi=function () {

    var total = 0;

    for (var i = 0, c; c = arguments[i++];) {

        total += c;

    }

    return total;

};

var sum = currying(multi);  

sum(100,200)(300);

sum(400);

console.log(sum());     // 1000  (空白調用時才真正計算)

增加適用性的柯里化

上面我的介紹的是柯里化延遲執行,縮小范圍!還有一種是增加了函數的適用性,但同時也降低了函數的適用范圍。其通用寫法如下:

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 map(handeler, list) {

    return list.map(handeler);

}

// 數組的每一項平方

map(square, [1, 2, 3, 4, 5]);

map(square, [6, 7, 8, 9, 10]);

map(square, [10, 20, 30, 40, 50]);

例子中,創建了一個map通用函數,用于適應不同的應用場景。顯然,通用性不用懷疑。同時,例子中重復傳入了相同的處理函數:square。

柯里化改造:

function square(i) {

    return i * i;

}

function map(handeler, list) {

    return list.map(handeler);

}

var mapSQ = currying(map, square);

mapSQ([1, 2, 3, 4, 5]);

mapSQ([6, 7, 8, 9, 10]);

mapSQ([10, 20, 30, 40, 50]);

Function.prototype.bind 方法也是柯里化應用

與 call/apply 方法直接執行不同,bind 方法 將第一個參數設置為函數執行的上下文,其他參數依次傳遞給調用方法(函數的主體本身不執行,可以看成是延遲執行),并動態創建返回一個新的函數, 這符合柯里化特點。

var foo = {x: 888};

var bar = function () {

    console.log(this.x);

}.bind(foo);               // 綁定

bar();  

關于函數柯里化就說到這里。與函數柯里化相對于的是反柯里化。


本站文章均為深正網站建設摘自權威資料,書籍,或網絡原創文章,如有版權糾紛或者違規問題,請即刻聯系我們刪除,我們歡迎您分享,引用和轉載,但謝絕直接搬磚和抄襲!感謝...
關注深正互聯
七星彩头尾 机顶盒怎样赚钱 小鱼赚钱怎么下载不了 学生如何微信赚钱方法 超市和饭店送什么货赚钱 股市赚钱的数学原理 李逵劈鱼捕鱼机漏洞规律技巧破解 教师直播间最能赚钱 谁玩滑板赚钱了 亿宝彩票苹果 怎么做淘宝刷屏赚钱 梦幻西游哪个等级赚钱 飞镖比赛能赚钱么 卢克团名打什么我想赚钱 街机海王捕鱼免费版 打白板变麻将的技巧 高智商赚钱软件