During past years utility libraries like Underscore and lodash have found their way into the toolchain of many JavaScript programmers.
While those utility libraries might make the code easier for you to write, they don’t necessarily make the code simpler or easier to understand. People reading and maintaining the code are obliged to, in addition to knowing the language and its standard library, also know the utility library it’s using. Libraries come and go, and everyone has their own favorite. What’s the order of arguments for this map() function? Which library was it from again? Underscore (the perennial favorite), lodash (its speedy and versatile younger brother), Ramda (the more functional cousin that got the argument order right), or whichever cool abstraction you found today? If you’re a team, whose favorite library should you choose? How about choosing the standard library?
When you write simple code that uses the standard library functions, you are making it easier for the future user and maintainer (who might as well be you) to understand. Yes, it might be more characters to type, but since when is writing speed the bottleneck in writing good, maintainable software?
It’s much easier to recover from no abstraction than the wrong abstraction. —Sebastian Markbåge
It’s a lot easier to refactor verbose code with few abstractions than concise code with the wrong abstractions. When you start seeing patterns in your code repeated over and over, it’s time to abstract, but now you can tell what is the right abstraction and it’s more likely you end up with something that is worth the additional overhead every abstraction adds.
JavaScript is evolving and new ES2015 and ES2016 editions (previously known as ES6 and ES7, respectively) pack a bunch of new features and Babel makes it very easy to use of them today. These features make some previously essential functions from utility libraries obsolete.
Good resources for learning those new features include the Learn ES2015 page on Babel website and Understanding ECMAScript 6 book by Nicholas C. Zakas. Learning to fully utilize the language of the Web gives you a transferable skill. JavaScript is likely to stick around much longer than the utility library of the day.
This is not to say there is no a place for any utility library or function installed from npm anymore. It’s just that many utility functions that were almost essential in order to be productive when we were writing ES3 are not needed anymore and today you might be better off just using plain JavaScript to do those tasks.
You might not need Underscore.
Examples
These examples illustrate some things ES5.1, ES2015 and ES2016 have made so easy that you might not need an utility library anymore.
What do I need to use them?
ES5 is now natively supported by all current browsers and Node.js. ES2015 and ES2016 examples can be compiled to ES5 with Babel. Babel is easy to add to almost any common JavaScript build tool in existence or to use with the require hook on Node.js. If you need to support legacy browsers (IE8, released 6 years ago) you can still get most of ES5 with a polyfill like es-shim.
Arrays
Iterate
-
Underscore
_.each(array, iteratee) -
ES5.1
array.forEach(iteratee)
Map
-
Underscore
_.map(array, iteratee) -
ES5.1
array.map(iteratee)
Find
-
Underscore
_.find(array, predicate) -
ES2015
array.find(predicate)
Get a property from each element in an array
-
Underscore
_.pluck(array, propertyName) -
ES2015
array.map(value => value[propertyName])
Check if array includes an element
-
Underscore
_.contains(array, element) -
ES2016
array.includes(element)
Convert an array-like object to array
-
Underscore
_.toArray(arguments) -
ES2015
Array.from(arguments)
Create a copy of an array with all falsy values removed.
-
Underscore
_.compact(array) -
ES2015
array.filter(x => !!x)
Create a copy of an array with duplicates removed
-
Underscore
_.uniq(array) -
ES2015
[...new Set(array)]
Find the index of a value in an array
-
Underscore
_.indexOf(array, value) -
ES5.1
array.indexOf(value)
Create an array with n numbers, starting from x
-
Underscore
_.range(x, x + n) -
ES2015
Array.from({ length: n }, (v, k) => k + x)
Objects
Names of own enumerable properties
-
Underscore
_.keys(object) -
ES5.1
Object.keys(object)
Names of all enumerable properties
-
Underscore
_.allKeys(object) -
ES2015
Reflect.enumerate(object) // Returns an Iterator
Values
-
Underscore
_.values(object) -
ES5.1
Object.keys(object).map(key => object[key])
Create a new object with the given prototype
-
Underscore
_.create(proto, propertiesObject) -
ES5.1
Object.create(proto, propertiesObject)
Create a new object from merged properties
-
Underscore
_.extend({}, source, { a: false }) -
ES2016
{ ...source, a: false }
Create a shallow clone of an object
-
Underscore
_.clone(object) -
ES2016
{ ...object }
Check if an object is an array
-
Underscore
_.isArray(object) -
ES5.1
Array.isArray(object)
Check if an object is a finite Number
-
Underscore
_.isFinite(object) -
ES2015
Number.isFinite(object)
Functions
Bind a function to an object
-
Underscore
foo(function () { this.bar(); }.bind(this)); foo(_.bind(object.fun, object)); -
ES2015
foo(() => { this.bar(); }); foo(object.fun.bind(object)); -
ES2016
foo(() => { this.bar(); }); foo(::object.fun);
Utility
Identity function
-
Underscore
_.identity -
ES2015
value => value
A function that returns a value
-
Underscore
const fun = _.constant(value); -
ES2015
const fun = () => value;
The empty function
-
Underscore
_.noop() -
ES2015
() => {}