偏函数

引子

在 JavaScript 中,有这么一个 API -- Number.parseInt(string[, radix]):根据指定的进制数将字符串转换成对应整数:

parseInt("16", 10); // => 16
parseInt("16", 16); // => 22
parseInt("100", 2); // => 4

那么我们想分别封装的对应的进制转换操作呢?

function parseInt10(string) {
  return parseInt(string, 10);
}

function parseInt2(string) {
  return parseInt(string, 2);
}

function parseInt16(string) {
  return parseInt(string, 16);
}

这种封装还不具有函数式的抽象,我们更希望是下面这种封装:

var parseInt10 = factory(parseInt, 10);
var parseInt2 = factory(parseInt, 2);
var parseInt16 = factory(parseInt, 16);

factory 是一个工厂函数,且是一个高阶函数,他将完成:

接受一个函数,传递给该函数部分参数,返回一个新的函数。让新的函数继续接受参数:

即,新函数是原函数的一部分。

function factory(sourceFunc) {
  // 获得参数数组
  var args = Array.prototype.slice.call(arguments, 1);
  // 返回新的函数
  return function() {
    // 新的函数继续接受参数
    var newArgs = Array.prototype.slice.call(arguments, 0);
    return sourceFunc.apply(this, newArgs.concat(args));
  }
}

测试一下:

var parseInt2 = factory(parseInt, 2);
parseInt2('100'); // => 4

上面的工厂函数我们就称之为偏函数(partial)partial 反映了新函数是原函数的一部分。underscore 的 _.partial 就能返回一个偏函数。

_.partial

_.partial(func, ...args):偏应用一个函数 func

源码

_.partial = restArgs(function (func, boundArgs) {
    var placeholder = _.partial.placeholder;
    // 返回一个partial后的新函数
    var bound = function () {
        // position用来标识当前赋值的arguments最新位置
        var position = 0, length = boundArgs.length;
        // 初始化新函数执行的参数
        var args = Array(length);
        for (var i = 0; i < length; i++) {
            // 对于最终调用时的位置`i`的参数
            // 如果绑定参数的对应位置是占位符,代表略过,以新的参数赋值之,并刷新最新的赋值位置`position`
            // 否则以绑定参数赋值之
            args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i];
        }
        // 如果arguments还没有被消费完, 则剩余arguments全部灌入args
        while (position < arguments.length) args.push(arguments[position++]);
        // 执行绑定函数的时候, 不改变上下文
        return executeBound(func, bound, this, this, args);
    };
    return bound;
});

相比较我们缩写的偏函数,undersocre 提供的偏函数还支持传递占位符,占位符用来标识不想被初始化的参数。

用例

var subtract = function(a, b) {
  return b - a;
};

sub5 = _.partial(subtract, 5);sub5(20);
// => 15

// 通过使用了一个placeholder(默认被理解的占位符为_), 我们这次先赋值了b, 暂缓了对a的赋值
subFrom20 = _.partial(subtract, _, 20);
subFrom20(5);
// => 15

results matching ""

    No results matching ""