老席老席杂货铺
Picture of github

Partila Function and currying

Partial Application (Partial Function Application)

定义

In computer science, partial application (or partial function application) refers to the process of fixing a number of arguments to a function, producing another function of smaller arity.

Partially applying a function means creating a new function by pre-filling some of the arguments to the original function.

看下面的示例

const add = function(a, b, c) {
  return a + b + c
}

let addOne = add.bind(null, 1)
let addThree = addOne.bind(null, 2) // 等价于 let addThree = add.bind(null, 1, 2)

addThree(3) // 6

上面的代码中,在调用的时候只需要传一个参数,在之前的Javascript 基础之 Bind 中详细介绍了 bind 的用法,所以用 bind 来实现偏函数是一个比较简单的方式。

下面我们提供一个更加通用的实现方法

const add = function(a, b, c) {
  return a + b + c
}

function partial(fn, ...args) {
  return function() {
    return fn(...args, ...arguments)
  }
}

let addOne = partial(add, 1)
addOne(2, 3) // 6
let addThree = partial(add, 1, 2)
addThree(3) // 6

Currying

定义

In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument.

// add = a => b => Number
const add = a => b => a + b;
const result = add(2)(3); // => 5

下面的代码是在JavaScript专题之函数柯里化中看到的一个牛人写的方法,让我们特别清楚的理解 Curry 的原理与实现,特此记录在这个地方,以供以后不时拿来盘盘

var curry = fn => {
  judge = (...args) => {
    args.length === fn.length
        ? fn(...args)
        : (arg) => judge(...args, arg)
  }
}

作用

  • 参数复用

    从上面的例子中不难看出,参数复用或者叫绑定部分参数,PartialCurrying 都有这样的功能。从上面的例子中,我们可以看到,通过 Partial 或者 Currying 返回的函数,实际上是把之前的参数通过闭包的方式保留了下来,在真正执行的时候再拿到,从而实现复用的效果

  • 提前返回

    上面的例子中也体现了这部分的作用,但是具体在使用中,在什么情况下需要提前返回呢?看下面的代码

    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);
            });
        }
    }
    

    当调用 addEvent 的时候,每次 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);
                });
            };
        }
    })();
    

    当然,上面的代码只是为了说明作用,做演示用,我们在真正开发过程中其实可以不用立即执行函数,从而使代码更直观。

  • 延迟执行

    其实从上面的代码中,我们可以看到,不论是 Partial 还是 Currying,实际上都是将一些参数保留下来,最后才最真正的计算。但是实际的作用是什么呢?其实通过我们之前用 bind 的实现可以了解一二。 bind 的作用实际上是提前改变函数上下文,延迟执行,通过这个例子是不是就容易理解了

参考链接