You Might Not Need Underscore
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)
Use a function to accumulate a single value from an array (left-to-right)
-
Underscore
_.reduce(array, iteratee, memo)
-
ES5.1
array.reduce(iteratee, memo)
Use a function to accumulate a single value from an array (right-to-left)
-
Underscore
_.reduceRight(array, iteratee, memo)
-
ES5.1
array.reduceRight(iteratee, memo)
Test whether all elements in an array pass a predicate
-
Underscore
_.every(array, predicate)
-
ES5.1
array.every(predicate)
Test whether some element in an array passes a predicate
-
Underscore
_.some(array, predicate)
-
ES5.1
array.some(predicate)
Find a value in an array
-
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
_.includes(array, element)
-
ES2016
array.includes(element)
Convert an array-like object to array
-
Underscore
_.toArray(arguments)
-
ES2015
[...arguments]
Convert an array of keys and values to an object
-
Underscore
_.object(array)
-
ES2015
array.reduce((result, [key, val]) => Object.assign(result, {[key]: val}), {})
-
Object Rest/Spread (Stage 2)
array.reduce((result, [key, val]) => {...result, [key]: val}, {})
Create a copy of an array with all falsy values removed
-
Underscore
_.compact(array)
-
ES5.1
array.filter(Boolean)
-
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)
Find the index in an array by predicate
-
Underscore
_.findIndex([4, 6, 7, 12], isPrime);
-
ES2015
[4, 6, 7, 12].findIndex(isPrime);
Create an array with n numbers, starting from x
-
Underscore
_.range(x, x + n)
-
ES2015
Array.from(Array(n), (_, i) => x + i)
Objects
Names of own enumerable properties as an array
-
Underscore
_.keys(object)
-
ES5.1
Object.keys(object)
Number of keys in an object
-
Underscore
_.size(object)
-
ES5.1
Object.keys(object).length
Names of all enumerable properties as an array
-
Underscore
_.allKeys(object)
-
ES2015
[...Reflect.enumerate(object)]
Values
-
Underscore
_.values(object)
-
ES2015
Object.keys(object).map(key => object[key])
Create a new object with the given prototype and properties
-
Underscore
_.create(proto, properties)
-
ES2015
Object.assign(Object.create(proto), properties)
Create a new object from merged own properties
-
Underscore
_.assign({}, source, { a: false })
-
ES2015
Object.assign({}, source, { a: false })
-
Object Rest/Spread (Stage 2)
{ ...source, a: false }
Create a shallow clone of own properties of an object
-
Underscore
_.extendOwn({}, object)
-
Object Rest/Spread (Stage 2)
{ ...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
_.isNumber(object) && _.isFinite(object)
-
ES2015
Number.isFinite(object)
Functions
Bind a function to an object
-
Underscore
foo(_.bind(function () { this.bar(); }, this)); foo(_.bind(object.fun, object));
-
ES2015
foo(() => { this.bar(); }); foo(() => object.fun());
Utility
Identity function
-
Underscore
_.identity
-
ES2015
value => value
A function that returns a value
-
Underscore
_.constant(value)
-
ES2015
() => value
The empty function
-
Underscore
_.noop
-
ES2015
() => {}
Get the current time in milliseconds since the epoch
-
Underscore
_.now()
-
ES5.1
Date.now()
Template
-
Underscore
var greeting = _.template("hello <%= name %>"); greeting({ name: 'moe' });
-
ES2015
const greeting = ({ name }) => `hello ${name}`; greeting({ name: 'moe' });
Did you find a bug? Do you have more examples of things that previously might have required an utility library, but not anymore? Send us a pull request on GitHub!