JS_函数柯里化

JS_函数柯里化

口罩.jpg

与函数绑定密切相关的主体是函数柯里化function currying),它用于创建已经设置好了一个或多个参数的函数。

函数柯里化的基本方法和函数绑定是一样的:受用一个闭包返回一个函数。两者的区别在于,当函数被调用的时候,返回的函数还需要设置一些传入的参数。eg:

function add(num1, num2){
    return num1 + num2;
}
function curriedAdd(num2){
    return add(5 , num2);
}
alert(add(2 , 3));  //5
alert(curriedAdd(3));   //8

上面这段代码定义了两个函数:addcurriedAdd()。后者的本质上是在任何情况下第一个参数为5的add()版本。

尽管从技术上来说currieAdd()并非是柯里化函数,但它很好地展示了这一概念。

柯里化函数通常由以下步骤动态创建:调用另一个函数并为它传入要柯里化的函数和必要参数。下面是创建柯里化函数的通用方式:

function curry(fn){
    //获取第一个之后的所有参数,返回值为数组
    var args = Array.prototype.slice.call(arguments, 1);
    return function(){
        //在内部函数中,存放所有传入的参数
        var innerArgs = Array.prototype.slice.call(arguments);
        var finalArgs = args.concat(innerArgs);
        return fn.apply(null, finalArgs);
    }
}

上面curry函数的主要作用是将返回函数的参数进行排序。curry()的第一个参数是要进行柯里化的函数,其他参数是要传入的值。

没有考虑到执行环境,所以调用apply时的第一个是null。curry()函数可以按照以下方式应用。

function add(num1, num2){
    return num1 + num2;
}

var curriedAdd = curry(add, 5);
alert(curriedAdd(3));

首先,创建了第一个参数,绑定为5的add()的柯里化版本。当调用curriedAdd()并传入3时,3会成为add的第二个参数,最后结果便是8。当然也可以像下面例子这样给出所有的函数参数。

function add(num1, num2){
    return num1 + num2;
}

var curriedAdd = curry(add, 5, 12);
alert(curriedAd());     //17

在这里,柯里化的add()函数两个参数都提供了,所以以后就无需再传递它们了。

  • curry还可以实现函数连缀调用(使用桥接模式)
function curry(fn) {
    var arr = [];
    return function () {
        if (arguments.length > 0) {
            arr = arr.concat(Array.from(arguments));
            return arguments.callee;
        } else {
            return fn.apply(null, arr);
        }
    }
}

// 桥接模式
var fn = curry(function () {
    var arr = Array.from(arguments);
    var sum = arr.reduce(function (value, item) {
        return value + item;
    });
    return sum;
})
fn(10)(20)(30);
var sum = fn();
console.log(sum);   //60
  • 当然还可以写在prototype原型上
Function.prototype.curry = function () {
    var arr = [];
    var self = this;
    return function () {
        if (arguments.length > 0) {
            arr = arr.concat(Array.from(arguments));
            return arguments.callee;
        } else {
            return self.apply(null, arr);
        }
    }
}

function getSum() {
    var arr = Array.from(arguments);
    var sum = arr.reduce(function (value, item) {
        return value + item;
    });
    return sum;
}

var fn = getSum.curry();
fn(10)(20);
fn(30, 40, 50);
fn(100);
var sum = fn();
console.log(sum);   //250

函数柯里化还常常作为函数绑定的一部分包含在其中,构造出更为复杂的bind()函数。eg:

function bind(fn, context){
    var args = Array.prototype.slice.call(arguments, 2);
    return function(){
        var innerArgs = Array.prototype.slice.call(arguments);
        var finalArgs = args.concat(innerArgs);
        return fn.apply(context, finalArgs);
    }
}

对curry()函数的主要更改在于传入的参数个数,当使用bind()时,它会返回绑定到给定环境的函数。

当你想除了event对象再额外给事件处理程序传递参数时,这非常有用,例如:

var handler = {
    message:"Event handled",
    
    handleClick:function(name, event){
        alert(this.message + ":" + name + ":" + event.type);
    }
};
var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler, "my-btn"));
  • EventUtil中的addHandler()方法
addHandler: function(element, type, handler){
    if (element.addEventListener){
        element.addEventListener(type, handler, false);
    } else if (element.attachEvent){
        element.attachEvent("on" + type, handler);
    } else {
        element["on" + type] = handler;
    }
}

在这个更新过的例子中,handle.handleClick()方法接受了两个参数:要处理的元素的名字和event对象。作为第三个参数传递给bind()函数的名字,以被传递给了handler.handleClick(),而handler.handleClick()也会同时接收到event对象。

ES5的bind()方法也实现函数柯里化,只要在this的值之后再传入另一个参数即可。

var handler = {
    message:"Event handled",
    
    handleClick:function(name, event){
        alert(this.message + ":" + name + ":" + event.type);
    }
};
var btn = document.getElementById("my-btn");
EventUtil.addhandler(btn, "click", hanlder.handleClick.bind(handler, "my-btn"));

JavaScript中的柯里化函数和绑定函数提供了强大的动态函数创建功能。使用bind()还是curry()要根据是否需要object对象来决定。它们能用于创建复杂的算法和功能,当然两者都不能滥用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351