Lodash和Underscore.js的区别

1721

为什么有些人更喜欢使用LodashUnderscore.js实用工具库?

Lodash似乎是Underscore的替代品,但后者已经存在更长时间。

我认为两者都很出色,但我不了解它们的工作原理,无法做出有根据的比较,我想了解它们之间的区别。


2
你可能想要看一下 lodash 的 GitHub 页面上链接的一些关于它的屏幕录像。个人而言,我一直在使用 underscore.js,但更多是因为我最开始就是用的它,正如你所说,它已经存在了很长时间。 - Jack
28
现在,lodashunderscore正在合并线程下。 - zangw
截至2023年,lodash的下载量比underscore更广泛,为5000万次下载,而underscore仅为1000万次。 - user3064538
12个回答

2158
我创建了 Lodash,旨在为数组、字符串、对象和 arguments 对象提供更一致的跨环境迭代支持1。它已成为 Underscore.js 的超集,提供更一致的 API 行为,更多的 功能(如 AMD 支持、深度克隆和深度合并),更全面的 文档 和单元测试(在 Node.jsRingoJSRhinoNarwhalPhantomJS 和浏览器中运行的测试),更好的大型数组/对象迭代性能和优化,以及更多的 自定义构建 和模板预编译工具的灵活性。

因为Lodash的更新频率比Underscore.js更高,因此提供了一个lodash underscore构建版本以确保兼容最新稳定版本的Underscore.js。

曾经我甚至被授予推进访问权限到Underscore.js, 部分原因是因为Lodash负责解决超过30个问题;在Underscore.js v1.4.x +中实现了错误修复,新功能和性能提升。

此外,至少有三个Backbone.js样板文件默认包含Lodash,并且Lodash现在在Backbone.js的官方文档中被提及。

查看Kit Cambridge的文章,Say "Hello" to Lo-Dash,深入了解Lodash和Underscore.js之间的区别。

注释:

  1. Underscore.js对数组、字符串、对象和arguments对象的支持不一致。在较新的浏览器中,Underscore.js方法忽略数组中的空洞,“对象”方法迭代arguments对象,字符串被视为类似数组的对象,方法正确地迭代函数(忽略它们的“prototype”属性)和对象(迭代像“toString”和“valueOf”这样的遮蔽属性),而在旧版浏览器中则不会。此外,Underscore.js方法(如_.clone)保留数组中的空洞,而其他方法(如_.flatten)则不会。

186
在开发 Lo-Dash 的过程中,我一直在思考这样一个问题:“与 Underscore 相比,有哪些地方可能会让人对 Lo-Dash 产生负面评价?” 然后我就会针对这些问题进行处理。因此,我增加了文档的内容,添加了自定义构建功能,并使源代码更易于阅读。 - John-David Dalton
10
我很想发布一些基准测试,但那会变得单调乏味。简单地说,我所运行的每个基准测试都证明了Lo-Dash比underscore快(在许多情况下要快得多)。 - Wil Moore III
190
我喜欢 Lo-Dash 并正在使用它,所以请不要认为我在抨击它,但是为什么不贡献给 Underscore 而是创建一个新的库呢? - Xananax
139
@Xananax - 请查看评论线程:https://github.com/jashkenas/underscore/commit/4e4bc194c0a0e06aa8f7633695ad10030d871a2b - 这可能会回答你的问题。 - Rob Grant
42
有没有将lodash合并回underscore的努力? - streetlight
显示剩余18条评论

202
Lodash受Underscore.js的启发,但现在是一种更优秀的解决方案。你可以创建自定义构建,具有更高的性能,支持AMD,并且具备出色的额外功能。在jsperf上查看Lodash vs. Underscore.js的基准测试和...这篇关于Lodash的精彩文章
当你处理集合时,最有用的功能之一是简写语法:
(虽然Underscore现在也支持此语法)
var characters = [
  { 'name': 'barney', 'age': 36, 'blocked': false },
  { 'name': 'fred',   'age': 40, 'blocked': true }
];

// Using "_.filter" callback shorthand
_.filter(characters, { 'age': 36 });

// Using Underscore.js
_.filter(characters, character => character.age === 36);

// → [{ 'name': 'barney', 'age': 36, 'blocked': false }]

(摘自Lodash文档

1
Kit Cambridge的博客链接非常有信息量。 - Brian M. Hunt
我认为这个(pluck示例)是错误的。从最后一次更新1.8.3开始,您可以像lodash一样使用pluck。无论如何,在以前的版本中,我不认为underscore会公开一个与map相同的函数(您的underscore示例似乎是一个map函数)。 - alexserver
8
Underscore自2012年起就有了“filter”功能(其名称为“where”),请参见https://github.com/jashkenas/underscore/issues/648。 - Muhammad Hewedy
在 Lo-Dash vs Underscore 基准测试链接上,我收到了 500 错误。 - Hylle
4
characters.filter(x=>x.age==36) //pure js - Alex Szücs
@AlexSzücs 避免使用 x 作为变量名,更推荐使用 character 以增加描述性。 - neiker

112
如果像我一样,你期望看到Underscore.js和Lodash之间用法差异的列表,那么可以查看从Underscore.js迁移到Lodash的指南。以下是当前的指南内容:
  • Underscore的_.any对应Lodash的_.some
  • Underscore的_.all对应Lodash的_.every
  • Underscore的_.compose对应Lodash的_.flowRight
  • Underscore的_.contains对应Lodash的_.includes
  • Underscore的_.each不支持通过返回false退出
  • Underscore的_.findWhere对应Lodash的_.find
  • Underscore的_.flatten默认为深度拍平,而Lodash是浅拍平
  • Underscore的_.groupBy支持一个回调函数,传入参数为(value, index, originalArray), 而在Lodash中,_.groupBy的回调函数仅传递一个参数:(value)
  • 在Underscore.js中,带有第三个参数undefined_.indexOf对应Lodash的_.indexOf
  • 在Underscore.js中,带有第三个参数true_.indexOf对应Lodash的_.sortedIndexOf
  • Underscore的_.indexBy对应Lodash的_.keyBy
  • Underscore的_.invoke对应Lodash的_.invokeMap
  • Underscore的_.mapObject对应Lodash的_.mapValues
  • Underscore _.max将Lodash的_.max_.maxBy结合在一起
  • Underscore _.min将Lodash的_.min_.minBy结合在一起
  • Underscore _.sample将Lodash的_.sample_.sampleSize结合在一起
  • Underscore _.object将Lodash的_.fromPairs_.zipObject结合在一起
  • Underscore _.omit通过断言函数与Lodash的_.omitBy相同
  • Underscore _.pairs是Lodash的_.toPairs
  • Underscore _.pick通过断言函数与Lodash的_.pickBy相同
  • Underscore _.pluck是Lodash的_.map
  • Underscore _.sortedIndex将Lodash的_.sortedIndex_.sortedIndexOf结合在一起
  • Underscore _.uniq通过迭代器与Lodash的_.uniqBy相同
  • Underscore _.where是Lodash的_.filter
  • Underscore _.isFiniteNumber.isFinite不匹配
    (例如,_.isFinite('1')在Underscore.js中返回true,但在Lodash中返回false
  • Underscore _.matches简写不支持深度比较
    (例如,_.filter(objects, { 'a': { 'b': 'c' } })
  • Underscore ≥ 1.7 & Lodash _.template 语法为
    _.template(string, option)(data)
  • Lodash _.memoize 缓存使用类似 Map 的对象
  • Lodash 不支持很多方法的 context 参数,而是支持 _.bind
  • Lodash 支持隐式链接 (implicit chaining)惰性链接 (lazy chaining)快捷方式融合 (shortcut fusion)
  • Lodash 将其重载的 _.head_.last_.rest_.initial 拆分成
    _.take_.takeRight_.drop_.dropRight
    (例如:在 Underscore.js 中,_.head(array, 2) 相当于 Lodash 中的 _.take(array, 2)

  • 1
    我在迁移时也遇到了这些问题,现在正在维护一个(WIP)跨文档来回转换。希望对其他人也有所帮助! - luxon
    1
    不错的差异!★★★★★ - Mr. Polywhirl

    60

    除了John的答案,并且阅读了有关Lodash的信息(我之前一直认为这是一个对Underscore.js的模仿),观看了性能测试、阅读了源代码以及博客文章,使Lodash比Underscore.js更卓越的几点如下:

    1. 重要的不是速度,而是速度的一致性(?)

    如果你查看Underscore.js的源代码,你会在最前面的几行看到Underscore.js回退到许多函数的本机实现。虽然在理想情况下,这可能是一个更好的方法,但是如果你查看这些幻灯片中提供的一些性能链接,很容易得出结论,这些“本机实现”的质量在浏览器之间变化很大。Firefox在某些功能上非常快,而Chrome在某些功能上占优势。(我想在某些场景下Internet Explorer也会占优势)。我认为最好选择一个在各种浏览器中性能更加一致的代码。

    请阅读之前的博客文章,不要因为我的缘故而相信它,请自己运行基准测试来判断。我现在感到震惊,甚至在Chrome的Array.every等简单的本机函数中,看到Lodash的性能比Underscore.js快100-150%!

    1. Lodash中的额外功能也非常有用。
  • 关于Xananax高赞评论提到对Underscore.js代码的贡献:拥有好的竞争对手总是更好的,它不仅保持了创新的动力,也促使您保持良好状态(或您的库)。
  • 这里是Lodash和Underscore.js之间的差异列表,它的Underscore.js构建是您现有项目的替代品。


    7
    “速度一致性”在哪种情况下会是一个价值观?假设我有一个在Firefox和IE中速度均为100%的方法,而本地实现在IE中速度为80%,在Firefox中速度为120%(或者反过来)。那么我会建议在Firefox中使用本地实现,在IE中使用自己的实现。我无法想象任何情况下会说:“让我们减慢Firefox的速度,只为了在那里与IE的速度一样”。代码大小和可维护性或所有浏览器的平均速度变慢可能是论据,但速度一致性呢? - stofl
    2
    我的意思是,“速度始终更快”。 - kumarharsh
    1
    下划线的占用空间较小,因为它主要是对本地函数进行包装。 - kumarharsh
    5
    我倾向于使用浏览器的本地实现,因为在大多数情况下它的性能是可以接受的,并且可以随着浏览器更新而改进,不必担心保持库的最新状态。 - orad
    3
    也许我表达不够清晰,我的意思是我倾向于使用一个内部使用本地函数的库(如果有的话),而不是总是优先选择它自己的实现。 - orad
    显示剩余7条评论

    44

    在2014年,我仍然认为我的观点是正确的:

    我个人认为,这次讨论有些过度了。引用上述博客文章中的话:

    大多数JavaScript实用程序库(如Underscore、Valentine和wu)都依赖于“本地优先双重方法”。这种方法优先选择本地实现,仅在本地等效物不受支持时才回退到原始JavaScript。但是jsPerf揭示了一个有趣的趋势:遍历数组或类似数组的集合的最有效方法是完全避免使用本地实现,而是选择简单的循环。

    好像“简单循环”和“原始JavaScript”比Array或Object方法的实现更本地化。天啊...

    "虽然拥有一个统一的真相来源会很好,但实际上并不存在。即使有人告诉你有一个“香草神”,亲爱的,我很抱歉,但这是不可能的。唯一能够成立的假设是我们都在编写 JavaScript 代码,并且希望在所有主要浏览器中都能够表现良好,尽管它们对同样的东西有不同的实现方式。说实话,这是一件非常麻烦的事情。但这就是前提,无论你喜欢与否。"
    "也许你们所有人都在处理需要像 Twitter 一样高效的大型项目,所以你们现在真的可以看到 在每秒列表迭代方面,850,000(Underscore.js)与2,500,000(Lodash)之间的差异!"
    我并不是这样的人。我的意思是,我曾经参与过需要解决性能问题的项目,但它们从未由Underscore.js或Lodash引起或解决。除非我掌握了实现和性能方面的真正差异(我们现在谈论的是C++),比如对可迭代对象(对象或数组,稀疏或非稀疏)进行循环,否则我宁愿不去理会任何基于已经有主观看法的基准平台结果的声明。
    只需要一次单个更新,比如Rhino,就足以使其Array方法的实现方式发生变化,以至于没有一个“中世纪循环方法执行得更好,永远等等”的牧师可以用自己的方式来辩解简单的事实,即突然间Firefox中的数组方法比他/她的主观脑残快得多。伙计们,你不能通过欺骗运行时环境来欺骗运行时环境!下次在推广{{blockquote}}你的实用工具包{{/blockquote}}时,请考虑这一点。
    因此,为了保持相关性:
    • 如果您追求方便而不牺牲原生体验,请使用Underscore.js。
    • 如果您追求方便并且喜欢其扩展功能目录(深度复制等),并且急需即时性能,最重要的是不介意在本地API超越主观解决方法时寻找替代方案,则使用Lodash。这很快就会发生。 期。
    • 甚至有第三种解决方案。自己动手!了解您的环境。了解不一致之处。阅读他们(John-DavidJeremy)的代码。不要使用此或那个,除非能够解释为什么真正需要一致性/兼容性层以增强工作流程或提高应用程序的性能。很可能您的要求可以通过简单的polyfill满足,您完全可以自己编写。两个库都只是普通的香草加上一点糖。 他们只是争夺谁提供最甜美的馅饼。但相信我,最终两者都只是在用水做饭。 没有香草神,所以也就没有香草教皇,对吧?

    选择最适合您需求的方法。通常情况下,我更喜欢实际实现上的回退而不是有偏见的运行时欺骗,但即使这也似乎是一种口味问题。坚持使用像http://developer.mozilla.comhttp://caniuse.com这样的优质资源,您就会没问题。


    感谢发布Lukas。内置函数可以进一步优化吗?我了解到它们受到标准的限制,防止它们具有与库相当的优化,但我不知道细节或这是否仍然是真实的。 - Brian M. Hunt
    通过为99%的使用情况进行优化,fast.js方法可以比其本机等效方法快5倍。 - Brian M. Hunt
    1
    嗨,布莱恩,如果我的话让你误解了,我很抱歉。我并不是说那些库比它们的本地等效物快不了多少。如果你现在迫切需要性能,你可能最好使用像LoDash或fast.js这样的工具包,因为它们提供了标准方法的更快实现。但是,如果你选择使用一个不依赖于本地方法的库,你可能会错过任何未来的内置性能优化。浏览器最终会发展的。 - Lukas Bünger
    4
    "制造商"很难让他们的浏览器符合标准,更不用说性能了。在原生实现中,大多数性能增益都是由于更快的硬件而产生的。 "原生实现将追赶上来" 的借口已经存在多年了。在互联网上,年份等同于永恒。 IF 原生实现有朝一日赶上来,那么库将会被更新以使用它们。这就是开源的酷处。如果应用程序开发人员不更新到最新库,他们的应用程序不会突然变慢,只是不会加速。 - Andrew Steitz
    2
    ...但如果你问他们关于Array.from的问题,他们可能甚至不知道它应该做什么。JS“实用工具”人似乎过分关注推广他们那些聪明绝顶的解决方案,以至于他们往往忘记了这样做实际上会稀释标准化过程。没有功能需求就没有对浏览器“制造商”的压力。有趣的事实是:4个主要浏览器中的2个基于开源项目([1](http://www.chromium.org/),[2](https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Source_Code/Mercurial))。 - Lukas Bünger
    显示剩余6条评论

    27

    我同意这里说的大部分事情,但我想指出支持Underscore.js的一个论点:库的大小。

    特别是在你开发的应用或网站主要在移动设备上使用的情况下,生成包的大小和对启动或下载时间的影响可能会起重要作用。

    为了比较,这些是我在运行Ionic serve后注意到的大小:

    Lodash: 523 kB
    Underscore.js: 51.6 kB
    

    使用BundlePhobia可以检查当前LodashUnderscore.js的大小。


    1
    谢谢Peter,这是一个值得注意的重要观点。还有更多的讨论在其他地方,包括:https://gist.github.com/alekseykulikov/5f4a6ca69e7b4ebed726。(通过链接一些其他的讨论并引用相关的部分,可以改进这个答案)通过选择lodash的子部分以及tree-shaking lodash,可以减少大小差异。 - Brian M. Hunt
    1
    感谢 @BrianM.Hunt 的回复,我不知道可以包含 lodash 的子部分,我会去看一下。最近,Ionic 也采用了这样的方式来处理其本地库,很好注意到越来越多的人关注应用程序大小问题。 - David Dal Busco
    2
    我想知道你从哪里得到了523kB?https://lodash.com/显示它只有24kB压缩。下载的文件只有74kB。 - MartianMartian
    1
    我的帖子是在2017年4月发布的。就像我在评论中所说的那样,运行ionic serve后使用source-map-explorer - David Dal Busco
    6
    2018年3月 - lodash.min.js 的大小为72.5 kB,underscore-min.js 的大小为16.4 kB。 - Combine

    11

    我不确定OP是什么意思,但我遇到了这个问题,因为我正在寻找从Underscore.js迁移到Lodash时需要记住的问题清单。

    如果有人发布了一篇完整的差异列表文章,我将不胜感激。让我从我已经学到的(也就是那些使我的代码在生产环境中崩溃的事情:/)开始:

    • _.flatten在Underscore.js中默认为深层次,你必须传递true作为第二个参数来使其变浅。在Lodash中,默认为浅层次,并且传递true作为第二个参数会使其变得深层次! :)
    • _.last在Underscore.js中接受第二个参数,用于告诉您要多少个元素。在Lodash中没有这样的选项。您可以使用.slice来模拟这个功能。
    • _.first(同样的问题)
    • _.template在Underscore.js中有多种用法之一是提供模板字符串和数据并获取HTML回来(或者至少以前是这样的)。在Lodash中,您将收到一个函数,然后应该将其与数据一起使用。
    • _(something).map(foo)在Underscore.js中起作用,但在Lodash中,我不得不将其重写为_.map(something,foo)。也许这只是一个TypeScript问题。

    4
    在 Lodash 中,使用链式调用可以传递一个惰性迭代器,并需要像 _(something).map(foo).value() 这样的终端点。 - Brian M. Hunt
    如果您使用 Backbone Collection 代理调用这些库,所有这些都可能会影响您 - 例如 collection.first(5) 不会给您前五个元素,而是第一个 :) - qbolec

    9

    Underscore vs Lo-Dash是本·麦考密克最新的一篇比较这两个库的文章:

    1. Lodash的API是Underscore.js的超集。
    1. 在底层,Lodash已经完全重写。
    1. Lodash绝对不会比Underscore.js更慢。
    1. Lodash添加了什么?
    • 增强的可用性
    • 额外的功能
    • 性能提升
    • 链接语法的速记语法
    • 自定义构建只使用所需内容
    • 语义化版本控制和100%代码覆盖率

    6

    4

    Lodash 提供了 _.mapValues() 方法,该方法与 Underscore.js 的 _.mapObject() 方法完全相同。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接