资讯专栏INFORMATION COLUMN

JS专题之函数柯里化

wudengzan / 2645人阅读

摘要:有这样的说法,并非柯里化有什么意义,而是,当函数可以作为函数的参数和返回值,成为函数式编程语言后,就会不可避免地产生函数柯里化。函数柯里化允许和鼓励你分隔复杂功能变成更小更容易分析的部分。那么用函数柯里化就能实现提前返回。

#### 前言
在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

#### 一、为什么会有函数柯里化?
Currying 的重要意义在于可以把函数完全变成「接受一个参数;返回一个值」的固定形式,这样对于讨论和优化会更加方便。

将关注的重点聚焦到函数本身,而不因冗余的数据参数分散注意力。

有这样的说法,并非柯里化有什么意义,而是,当函数可以作为函数的参数和返回值,成为函数式编程语言后,就会不可避免地产生函数柯里化。

#### 二、具体实现
先来一个简单的 add 函数

function add(x, y) {
    return x + y;
}

add(2, 3);  // 5

重要的概念多说一遍:函数柯里化就是接收多个参数的函数变换为接收一个函数,并返回接收余下参数,最终能返回结果的技术

那么,继续:

function add(x) {
    return function(y) {
        return x + y;
    }
}

add(2)(3);  // 5

所以,曾经的一个函数,因为闭包操作(返回函数并访问了自由变量的行为),变成了多个接收一个参数的函数。

所以简单来讲:函数柯里化就是意图将函数的参数变成一个。让函数可以输入一个值,就返回一个相对应的值,从而实现纯函数化。

为什么函数式编程要求函数必须是纯的,不能有副作用?因为它是一种数学运算,原始目的就是求值,不做其他事情,否则就无法满足函数运算法则了。在函数式编程中,函数就是一个管道(pipe)。这头进去一个值,那头就会出来一个新的值,没有其他作用。

所以良好的编程规范是尽可能让函数块做一个事情,实现可复用性,可维护性。

上面的例子中,如果有很多个参数怎么办,难道一层层嵌套?

我们继续:

function plus(value) {
    "use strict";
    var add = function () {
        var args = [];
        var adder = function adder() {
            Array.prototype.push.apply(args,Array.prototype.slice.apply(arguments))
            return adder;
        }
        adder.toString = function () {
            return args.reduce(function(a, b) {
                return a + b;
            })
        }
        return adder;
    }
    return add()(value);
}

plus(2)(3)(5).toString();  // 10;

上面的代码看起来不那么优雅,如果是减法,我们就得又重新为减法写这么多的代码。像 lodash, underscore 这些工具库,都提供了柯里化的工具函数。

我们一起来试着实现:

function curry(fn, args) {
    var length = fn.length;  // 函数参数的长度

    // 闭包保存参数列表
    args = args || [];

    return function() {
        // 获取参数列表。
        var _args = args.slice(0);
        
            Array.prototype.push.apply(_args, Array.prototype.slice.call(arguments))

        if (_args.length < length) {
        // 如果传入的参数列表长度还没有超过函数定义时的参数长度,就 push 新的参数到参数列表中保存起来。
        
            // 自己调用自己,将保存的参数传递到下一个柯里化函数。
            return curry.call(this, fn, _args);
        }
        else {
        // 如果传入的参数列表长度已经超过函数定义时的参数长度,就执行。
            return fn.apply(this, _args);
        }
    }
}
三、应用场景

函数柯里化的好处有几个:

参数复用;

提前返回;

延迟计算/运行。

函数柯里化允许和鼓励你分隔复杂功能变成更小更容易分析的部分。这些小的逻辑单元显然是更容易理解和测试的,然后你的应用就会变成干净而整洁的组合,由一些小单元组成的组合。

文章开篇的 add 函数,假如,每次调用加法有一个初始值会怎样?

var add = curry(function(a, b, c) {
    return a + b + c;
})

var addTen = add(10);

var addSix = add(6);

addTen(2)(3);  // 15;

addSix(7)(8);  // 21;

以上代码就实现了参数复用,保存固定参数的函数。

看一个经典的例子:
元素绑定事件监听器:

var addEvent = function(el, type, fn, capture) {
    if (window.addEventListener) {
        el.addEventListener(type, function(e) {
            fn.call(el, e);
        }, capture);
    } else if (window.attachEvent) {
        el.attachEvent("on" + type, function(e) {
            fn.call(el, e);
        });
    } 
};

以上代码是为了兼容 IE 浏览器对 DOM 事件绑定做的函数封装。

问题在于,每次对 DOM 元素进行事件绑定时,函数内部都会走一遍 if else。那么用函数柯里化就能实现提前返回

var addEvent = (function(){
    if (window.addEventListener) {
        return function(el, sType, fn, capture) {
            el.addEventListener(sType, function(e) {
                fn.call(el, e);
            }, (capture));
        };
    } else if (window.attachEvent) {
        return function(el, sType, fn, capture) {
            el.attachEvent("on" + sType, function(e) {
                fn.call(el, e);
            });
        };
    }
})();
总结

函数柯里化是“函数是一等公民”的编程语言环境形成的编程风格,利用了函数能作为参数一级返回值以及利用了闭包保存变量的特点,是将多个参数的函数转换为接收一个参数,最后返回结果的技术。

欢迎关注我的个人公众号“谢南波”,专注分享原创文章。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101449.html

相关文章

  • js 扩展 -- currying 柯里函数

    摘要:里也有柯里化的实现,只是平时没有在意。如果函数柯里化后虽然生搬硬套,不过现实业务也会有类似场景。 柯里化 先解释下什么是 柯里化 在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。 js 里也有柯里化的实现,只是平时没有在意。先把原文简介贴...

    Pocher 评论0 收藏0
  • JavaScript专题系列文章

    摘要:专题系列共计篇,主要研究日常开发中一些功能点的实现,比如防抖节流去重类型判断拷贝最值扁平柯里递归乱序排序等,特点是研究专题之函数组合专题系列第十六篇,讲解函数组合,并且使用柯里化和函数组合实现模式需求我们需要写一个函数,输入,返回。 JavaScript 专题之从零实现 jQuery 的 extend JavaScritp 专题系列第七篇,讲解如何从零实现一个 jQuery 的 ext...

    Maxiye 评论0 收藏0
  • JavaScript专题函数柯里

    摘要:一个经常会看到的函数的实现为第一版我们可以这样使用或者或者已经有柯里化的感觉了,但是还没有达到要求,不过我们可以把这个函数用作辅助函数,帮助我们写真正的函数。 JavaScript 专题系列第十三篇,讲解函数柯里化以及如何实现一个 curry 函数 定义 维基百科中对柯里化 (Currying) 的定义为: In mathematics and computer science, cu...

    zhangfaliang 评论0 收藏0
  • JavaScript专题函数

    摘要:专题系列第十四篇,讲解偏函数以及如何实现一个函数定义维基百科中对偏函数的定义为翻译成中文在计算机科学中,局部应用是指固定一个函数的一些参数,然后产生另一个更小元的函数。 JavaScript 专题系列第十四篇,讲解偏函数以及如何实现一个 partial 函数 定义 维基百科中对偏函数 (Partial application) 的定义为: In computer science, pa...

    beita 评论0 收藏0
  • JavaScript专题函数组合

    摘要:专题系列第十六篇,讲解函数组合,并且使用柯里化和函数组合实现模式需求我们需要写一个函数,输入,返回。这便是函数组合。 JavaScript 专题系列第十六篇,讲解函数组合,并且使用柯里化和函数组合实现 pointfree 模式 需求 我们需要写一个函数,输入 kevin,返回 HELLO, KEVIN。 尝试 var toUpperCase = function(x) { return...

    周国辉 评论0 收藏0

发表评论

0条评论

wudengzan

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<