Lodash 和 Ramda 的区别

Differences between Lodash and Ramda

所以我一直在研究一些 JS 框架。据我所见,两个最受欢迎的现代图书馆是 Lodash and Ramda(对吗?)。

我看到了关于 Lodash 和 Underscore 的 similar question。但是,我没有看到任何与 Lodash 和 Ramda 相关的内容。老实说,我也找不到任何基准。

有什么区别?用例怎么样?哪个更好?

Dislaimer:我是 Ramda 的创始人和首席维护者之一,所以我相信这个回答会包含一些个人偏见。我会努力不让它失控的。

概述

关于这部分问题:

Which one is better?

这里没有人可以明确地回答这个问题。这取决于您的要求、您的历史、您的编程品味以及一百万个甚至更多的模糊因素。

当然,我偏向于 Ramda,但我仍然不会试图告诉你哪个更好。

但是这两者在能力上有很大的重叠,在基本理念上也有很大的不同。

两者都是grab-bags效用函数,函数之间几乎没有内聚性。也就是说,它们是 libraries 而不是 frameworks。他们根本不会尝试确定您如何编写或组织代码。

它们有很多重叠。在 305 个当前的 lodash 函数和 261 个当前的 Ramda 函数中,有 103 个具有相同的名称,而且几乎总是具有相似的目的。其余函数的一半可能在其他库中以不同的名称找到。

简史

虽然之前有一些实验性的函数库,但 Underscore 是将其中许多概念带入 Javascript 主流的库。在 space.

中,它作为无可争议的领导者独树一帜了一段时间。

但是它的性能出现了问题。最终 lodash 的创建在很大程度上是为了尝试以更好的性能做同样的事情。通过创建像 map 这样的经典函数的自定义版本,而不是默认使用 JS 引擎的 built-in 函数,lodash 很快就能超越 Underscore。但它最初是作为 Underscore 的 drop-in 替代品,并在很长一段时间内保持了这种关注。

Ramda founders were impressed with the ideas in Reginald Braithwaite's JavaScript Allongé 并创建了 Ramda 作为一种教育工具,以帮助将这些想法变成一个实用的库。他们不太关注性能,而更关注干净的 API 和功能组合,以及不可变数据和 side-effect-free 编码。通过一系列奇怪的事件,它最终变得非常流行。

最近您很少听到有关 Underscore 的信息。它仍然在那里,并且仍然使用了一些,但你听不到太多的嗡嗡声。对于大多数用户来说,lodash 很好地填充了 space。 Ramda 增长很快,但似乎已经稳定在 lodash 获得的 the downloads 的 20 - 25% 左右。但是这三个库在功能和使用上都在继续增长,Underscore 和 Ramda 的使用率大致相同,远低于 lodash。

有一次,lodash 创建了一个版本,试图考虑 Ramda 的一些核心问题。我个人从未使用过 lodash-fp,所以我不能说它有多成功。

哲学

lodash

Lodash 专注于灵活性和性能。作为其创建者 once described,它的重点是

providing quality utility methods to as many devs as possible with a focus on consistency, compatibility, customization, and performance.

虽然没有 general-purpose 库可以像 custom-written 代码一样快,但 lodash 尽可能接近。

而且 lodash 很灵活。我wrote几年前

Consider lodash's filter: It accepts an array, object, or string for its collection, a function, object, string, or nothing at all for its callback, and an object or nothing at all for its thisArg. You're getting 3 * 4 * 2 = 24 functions in one!

拉姆达

Ramda 不太关心性能,更关心简单干净的 API 设计。 Ramda 的想法是一个函数应该只做一件事,并且应该有一个清晰的接口。 Ramda 的 filter 函数接受一个谓词函数和一个可过滤类型的对象,以及 returns 另一个该类型的对象。 (当然,这提出了什么构成可过滤类型的问题,但那是为了它的文档。)

Ramda 的重点是让函数的组合变得简单,就像所有数据都是不可变的一样工作,并避免 side-effects。它还包含其他函数式编程问题,例如提供镜头和使用 FantasyLand specification.

的代数类型

主要区别

  • Lodash(忽略 lodash-fp)函数大多首先获取它们的数据,然后是处理数据的那些东西,有时是改变行为的可选参数。 Ramda 将最不可能改变的论点放在最前面,最有可能改变的论点放在最后。这意味着在 data-transformation 函数中数据是最后的。 Ramda 完全避免了可选参数。

  • Ramda 柯里化了它的所有功能,以及它 returns 几乎所有的功能。 Lodash 有一个 curry 函数,但您需要显式调用它。这是 Ramda 关于函数组合的思想的核心。

  • Lodash 专注于 reference-equality。 Ramda 专注于 value-equality。所以虽然这些很相似:

    // lodash
    _.union ([1, 2, 3, 4, 5], [2, 3, 5, 7, 11]) //=> [1, 2, 3, 4, 5, 7, 11]
    _.intersection ([1, 2, 3, 4, 5], [2, 3, 5, 7, 11]) //=> [2, 3, 5]
    
    // Ramda
    R.union ([1, 2, 3, 4, 5], [2, 3, 5, 7, 11]) //=> [1, 2, 3, 4, 5, 7, 11]
    R.intersection ([1, 2, 3, 4, 5], [2, 3, 5, 7, 11]) //=> [2, 3, 5]
    

    这些行为非常不同:

    // lodash
    _.union (
      [{x: 1}, {x: 2}, {x: 3}, {x: 4}, {x: 5}], 
      [{x: 2}, {x: 3}, {x: 5}, {x: 7}, {x: 11}]
    )
    //=> [{x: 1}, {x: 2}, {x: 3}, {x: 4}, {x: 5}, {x: 2}, {x: 3}, {x: 5}, {x: 7}, {x: 11}]
    _.intersection (
      [{x: 1}, {x: 2}, {x: 3}, {x: 4}, {x: 5}], 
      [{x: 2}, {x: 3}, {x: 5}, {x: 7}, {x: 11}]
    ) //=> []
    
    // Ramda
    R.union (
      [{x: 1}, {x: 2}, {x: 3}, {x: 4}, {x: 5}], 
      [{x: 2}, {x: 3}, {x: 5}, {x: 7}, {x: 11}]
    )
    //=> [{x: 1}, {x: 2}, {x: 3}, {x: 4}, {x: 5}, {x: 7}, {x: 11}]
    R.intersection (
      [{x: 1}, {x: 2}, {x: 3}, {x: 4}, {x: 5}], 
      [{x: 2}, {x: 3}, {x: 5}, {x: 7}, {x: 11}]
    ) //=> [x: 2}, {x: 3}, {x: 5}]
    

    这种差异是显着的。 Ramda 的设计更紧密地对齐 与功能系统,但它有很大的涉及 equality-checking 时的订单价格。对于此类任务,Lodash 可能比 Ramda 快两个数量级。

  • Ramda 的主要设计目的是通过组合在短管线或长管线中构建函数。 Lodash 主要设计用于命令式代码。您可以将任一库用于任一作业,但典型的 Ramda 代码可能看起来像

    const myFn = R.pipe (
      R.fn1,
      R.fn2 ('arg1', 'arg2'),
      R.fn3 ('arg3'),
      R.fn4
    )
    

    等效的 lodash 代码可能看起来像

    const myFn = (x, y) => {
      const var1 = _.fn1 (x, y)
      const var2 = _.fn2 (var1, 'arg1', 'arg2')
      const var3 = _.fn3 (var2, 'arg3')
      return _.fn4 (var3)
    }
    
  • Lodash 对性能的关注意味着它将为任务提供许多 well-optimized 功能。例如,Lodash 具有所有这些功能:isArgumentsisArrayisArrayBufferisArrayLikeisArrayLikeObjectisBooleanisBuffer, isDate, isElement, isEqual, isEqualWith, isError, isFinite, isFunction, isInteger, isLengthisMapisMatchisMatchWithisNaNisNativeisNullisNumberisObject, isObjectLike, isPlainObject, isRegExp, isSafeInteger, isSet, isString, isSymbol, isTypedArray, isUndefined, isWeakMap, isWeakSet.

    相比之下,Ramda 希望您为常用函数提供更多参数。它只有 isisEmptyisNil。但它是 is 句柄 几乎所有上述情况,要么通过显式调用:is (Array, [1, 2, 3]),要么通过部分应用来创建可重用函数 const isArray = is (Array)

其中一些差异可能会随着 lodash-fp 消失。但我并没有真正意识到 lodash-fp 是景观的主要部分。而对于 lodash 本身,我们可以看到这些库非常不同。

性能

问题是关于基准的。我知道没有全面的基准套件。有趣的是,似乎除了涉及 value-equality 与 reference-equality 的问题外,lodash 在大多数任务中的速度要快 10-20%。 Ramda 不像 lodash hyper-optimized,但它在编写时考虑了性能,只要满足其他更基本的标准即可。

在那些 do 涉及 value-vs-reference 平等的情况下,lodash 团队可以指出更高的速度,但 Ramda 团队可能会回应说得到错误的答案很快就赢不了了。

目标受众

Underscore 帮助将函数式编程工具引入 Javascript。它是一个 general-purpose 实用程序库,专为任何想要提高工作效率的 JS 开发人员而设计。 Lodash 继承了这个重点。两者都是以开发人员人体工程学为中心编写的。

Ramda 的受众更加有限。它适用于那些不仅想要从函数式编程中获取特定工具,而且还想要获取函数纯度和数据不变性等更基本思想的人。它既针对想要转向更多 FP 风格的 JS 开发人员,也针对那些希望以熟悉的方式使用 JS 的 FP 语言开发人员。它以简单的中心焦点编写。

总结

这些库在功能上有很多重叠。但他们的设计、基本理念和开发人员体验却大不相同。

如果您在它们之间进行选择,根据上述内容,有一个适合您的正确答案,但没有适合每个人的明确明确的最佳库。

而且您还应该考虑是否需要一个。开发您自己的可重用函数列表以包含在任何需要它们的项目中并不难。该平台的功能比构想这些库时要强大得多。