函数式 js 接口完成道理,以及 lodash/fp 模块

函数式 js 接口

之前在 youtube 上看到一个手艺视频,讲“underscore.js的接口为何不好用”,以及什么样的接口更好用。演讲者是 lodash.js 的作者,他提出了一种“周全函数式”的 js 接口设想情势。也许相似如许:

// 传统接口
_.map([1, 2, 3], function (el) {return el * 2}); // return [2, 4, 6]

// 函数式接口
var fn = _.map([1, 2, 3]); // return a function
fn(function (el) {return el * 2}); // return [2, 4, 6];

// 或许
_.map([1, 2, 3])(function (el) {return el * 2}); // return [2, 4, 6];

找到一点觉得没有?实在就是函数式编程言语中普遍存在的“科里化”函数。当实参填满形参表的时刻,实行结算返回效果,不然返回一个暂时函数,继承吸收实参。

看到这个写法眼前一亮,觉得有大规模简化代码的潜力。当时现实试了一下发下许多处所用不了,由于之前写的代码受 jQuery 影响,有许多如许的接口:

foobar.attribute(name); // 读属性
foobar.attribute(name, newValue); // 写属性

如许的接口是根据上述要领 curry 化会使得读属性变得不能够,基本原因是参数数目差别时 attribute 函数的语义基本不一样。运用 jQuery 的时刻觉得这类写法异常爽,后来就随着这么写,然则现在看来如许的接口设想是有题目的。

言归正传,本日聊聊如许的接口怎样完成,以及 lodash 中的 fp 模块。

完成道理

说到底就是个 currying 的题目,currying 在许多言语中是内置功用,然则 js 没有,所以我们要完成一个 currying 东西函数。起首贴一个最浅易的 currying 完成,它的功用异常简朴,输入一个函数 fn1 和部份实参,返回一个保留部份实参,继承吸收实参的函数 fn2,挪用fn2,它会兼并实参数组,并挪用 fn1。

/**
 * 函数柯里化
 * @param fn 输入函数
 * @return 柯里化后的函数
 */
var curry = function (fn) {
    if (!isFunction(fn)) {
        return;
    }

    var args = slice(arguments, 1);
 
    return function () {
        return fn.apply(this, args.concat(slice(arguments, 0)));
    }
}

isFunction 和 slice 人人都晓得我就不贴了。看一下怎样挪用:

function add(a, b) {
    return a + b;
}

addOne = curry(add, 1);

addOne(2); // return 3

有时刻我们须要输入的部份实参是数组列表情势,所以我们包装一下适才的 curry 函数:

/**
 * 函数柯里化
 * @param fn 输入函数
 * @param arr 参数列表
 * @return 柯里化后的函数
 */
var curryApply = function (fn, arr) {
    if (!isFunction(fn)) {
        return;
    }

    var args = arr.slice(0);
    args.unshift(fn);
    return curry.apply(this, args);
}

上面的 curry 函数有个题目,就是一连屡次补充实参,我们还须要封装一个支撑一连挪用的版本:

/**
 * 自动柯里化
 * @param fn 输入函数
 * @param n 输入函数参数个数
 * @return 柯里化后的函数
 */
var autoCurry = function (fn, n) {
    if (!isFunction(fn)) {
        return;
    }

    function retFn() {
        var len = arguments.length;
        var args = slice(arguments, 0);
        var nextn = n - len;
 
        if (nextn > 0) {
            return autoCurry(curryApply(retFn, args), nextn);
        }
    
        return fn.apply(this, args);
    }
    
    return retFn;
}

autoCurry 运用的递归的要领,输出函数能够能够经由过程简朴挪用的体式格局一连补充实参,当实参和预设的参数数目相称时,实行输入函数。运用要领以下:

function compute(a, b, c) {
    return (a + b) * c;
}

var curryedCompute = autoCurry(compute, 3);

compute(1, 2, 3); // return 9
curryedCompute(1)(2)(3); // return 9

人人假如运用 node.js 的话,能够晓得 npm 中有个 curry 模块,完成的功用是一样的,差别的是当你不输入参数个数 n 时,curry 模块 会运用 Function 对象的 length 属性作为预设的 n 值。

lodash/fp

到这里完成道理就讲清晰了。本着不造轮子的准绳,假如人人想尝试一下函数式作风的基本 js 库的话,发起运用 lodash/fp 这个模块。人人都晓得 lodash 是 underscore 的 better implemention,而 lodash/fp 就是科里化的 lodash。与简朴的 currying 差别的是,为了方便运用,lodash/fp 的设想者调换了一些接口的参数递次,比方开首提到的 _.map 接口,假如简朴 currying 的话第一个参数应该是数组[1, 2, 3],然则大多数时刻,我们想要持有的是一个算法,用这个算法处置惩罚差别的数据。所以我们愿望暂存的现实上是第二个参数 fn,所以 lodash/fp 的接口是如许的:

// The `lodash/map` iteratee receives three arguments:
// (value, index|key, collection)
_.map(['6', '8', '10'], parseInt);
// → [6, NaN, 2]

// The `lodash/fp/map` iteratee is capped at one argument:
// (value)
fp.map(parseInt)(['6', '8', '10']);
// → [6, 8, 10]

关于 lodash/fp 更细致的申明,请看:https://github.com/lodash/lodash/wiki/FP-Guide

    原文作者:Y3G
    原文地址: https://segmentfault.com/a/1190000005760112
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞