JS专题之函数柯里化

前言

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

1、为何会有函数柯里化?

Currying 的重要意义在于能够把函数彻底变成「接受一个参数;返回一个值」的固定形式,这样对于讨论和优化会更加方便。java

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

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

2、具体实现

先来一个简单的 add 函数浏览器

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

add(2, 3);  // 5
复制代码

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

那么,继续:app

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);
        }
    }
}
复制代码

3、应用场景

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

  1. 参数复用;
  2. 提早返回;
  3. 延迟计算/运行。

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

文章开篇的 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);
            });
        };
    }
})();
复制代码

总结

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

掘金专栏 JavaScript 系列文章

  1. JavaScript之变量及做用域
  2. JavaScript之声明提高
  3. JavaScript之执行上下文
  4. JavaScript之变量对象
  5. JavaScript之原型与原型链
  6. JavaScript之做用域链
  7. JavaScript之闭包
  8. JavaScript之this
  9. JavaScript之arguments
  10. JavaScript之按值传递
  11. JavaScript之例题中完全理解this
  12. JavaScript专题之模拟实现call和apply
  13. JavaScript专题之模拟实现bind
  14. JavaScript专题之模拟实现new
  15. JS专题之事件模型
  16. JS专题之事件循环
  17. JS专题之去抖函数
  18. JS专题之节流函数

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

相关文章
相关标签/搜索