Underscore.js - Documentation

What is Underscore.js?

Underscore.js is a JavaScript library that provides a whole mess of helpful utility functions. It’s a foundational library that offers a consistent and robust set of tools for working with collections (arrays, objects), iterating, and performing common data manipulations. While it doesn’t directly interact with the DOM (unlike jQuery), it excels at providing the functional programming building blocks that make JavaScript development more efficient and readable. Think of it as a Swiss Army knife for your JavaScript toolkit.

Why Use Underscore.js?

Underscore.js offers several compelling reasons for inclusion in your projects:

Setting up Underscore.js

There are several ways to incorporate Underscore.js into your project:

<script src="underscore-min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/underscore@1.13.1/underscore-min.js"></script>
npm install underscore
# or
yarn add underscore

Then, import it into your JavaScript modules as needed (the specific import syntax depends on your module bundler). For example, with ES6 modules:

import _ from 'underscore';

Core Concepts and Terminology

Understanding these core concepts is crucial for effectively using Underscore.js:

By understanding these concepts and the functions detailed in the following sections of this manual, you will be well-equipped to harness the power of Underscore.js for your JavaScript projects.

Collections

Underscore.js provides a rich set of functions for working with collections (arrays and objects). These functions often utilize iterators and higher-order functions to perform efficient and expressive data manipulation.

each (alias: forEach)

Iterates over a list of elements, executing a provided function for each element. This function doesn’t return a value; it’s primarily used for side effects (e.g., modifying elements, logging output).

Signature:

_.each(list, iteratee, [context])

Example:

_.each([1, 2, 3], function(num) { console.log(num); }); // Logs 1, 2, 3 to the console.
_.each({a: 1, b: 2}, function(value, key) { console.log(key + ': ' + value); }); // Logs a: 1, b: 2

map (alias: collect)

Produces a new array of values by mapping each element in the input list through a transformation function.

Signature:

_.map(list, iteratee, [context])

Example:

var doubled = _.map([1, 2, 3], function(num){ return num * 2; }); // doubled will be [2, 4, 6]

reduce (alias: foldl, inject)

Boils down a list of values into a single value. It iteratively applies a function to each element and accumulates the result.

Signature:

_.reduce(list, iteratee, memo, [context])

Example:

var sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0); // sum will be 6

filter (alias: select)

Looks through each value in the list, returning an array of all the values that pass a truth test (predicate).

Signature:

_.filter(list, predicate, [context])

Example:

var evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); // evens will be [2, 4, 6]

reject

The opposite of filter; returns the values that do not pass the truth test.

Signature: Similar to filter.

Example:

var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); // odds will be [1, 3, 5]

every (alias: all)

Checks if all elements in the list satisfy the given predicate. Returns true if all do, false otherwise.

Signature: Similar to filter.

some (alias: any)

Checks if at least one element in the list satisfies the given predicate. Returns true if at least one does, false otherwise.

Signature: Similar to filter.

contains (alias: include)

Checks if the list contains the given value.

Signature:

_.contains(list, value, [fromIndex])

invoke

Calls a method on each value in the list. Useful for invoking methods on objects within an array.

Signature:

_.invoke(list, methodName, *args)

pluck

Extracts a list of property values from a list of objects.

Signature:

_.pluck(list, propertyName)

max

Returns the maximum value in the list.

Signature:

_.max(list, [iteratee])

min

Returns the minimum value in the list.

Signature: Similar to max.

sortBy

Sorts a list of values by a given property or function.

Signature:

_.sortBy(list, iteratee, [context])

groupBy

Groups a list of values into an object based on the result of a given function.

Signature:

_.groupBy(list, iteratee, [context])

indexBy

Similar to groupBy, but creates an object where the keys are the results of the iteratee and the values are the original elements.

Signature: Similar to groupBy.

countBy

Counts the number of occurrences of each unique result of an iteratee function.

Signature: Similar to groupBy.

shuffle

Randomly shuffles the elements in the list.

Signature:

_.shuffle(list)

sample

Returns a random element from the list. If a number is provided, returns an array of that many random elements.

Signature:

_.sample(list, [n])

toArray

Converts an object into an array of its values.

Signature:

_.toArray(obj)

size

Returns the number of elements in a collection.

Signature:

_.size(obj)

This section provides a concise overview of Underscore.js’s collection functions. Refer to the Underscore.js documentation for complete details and advanced usage examples.

Arrays

Underscore.js extends JavaScript’s built-in array functionality with several powerful utility functions. These functions simplify common array manipulations and improve code readability.

first

Returns the first element of an array. If a number n is provided, returns the first n elements as a new array.

Signature:

_.first(array, [n])

Example:

_.first([1, 2, 3]); // => 1
_.first([1, 2, 3], 2); // => [1, 2]

initial

Returns all elements of an array except the last one. If a number n is provided, returns all but the last n elements.

Signature:

_.initial(array, [n])

Example:

_.initial([1, 2, 3]); // => [1, 2]
_.initial([1, 2, 3, 4], 2); // => [1, 2]

rest (alias: tail)

Returns all elements of an array except the first one. If a number n is provided, returns all but the first n elements.

Signature:

_.rest(array, [n])

Example:

_.rest([1, 2, 3]); // => [2, 3]
_.rest([1, 2, 3, 4], 2); // => [3, 4]

last

Returns the last element of an array. If a number n is provided, returns the last n elements as a new array.

Signature:

_.last(array, [n])

Example:

_.last([1, 2, 3]); // => 3
_.last([1, 2, 3], 2); // => [2, 3]

compact

Creates a new array with all falsey values removed. Falsey values are false, null, 0, "", undefined, and NaN.

Signature:

_.compact(array)

Example:

_.compact([0, 1, false, 2, '', 3]); // => [1, 2, 3]

flatten

Flattens a nested array (array of arrays) into a single level. Can handle multiple levels of nesting.

Signature:

_.flatten(array, [shallow])

Example:

_.flatten([1, [2, [3, 4], 5]]); // => [1, 2, 3, 4, 5]
_.flatten([1, [2, [3, 4], 5]], true); // => [1, 2, [3, 4], 5]

without

Returns a new array without the given values.

Signature:

_.without(array, *values)

Example:

_.without([1, 2, 1, 0, 3, 1, 4], 0, 1); // => [2, 3, 4]

uniq (alias: unique)

Produces a duplicate-free version of the array, using a simple === comparison for equality. Uses a Set internally for efficiency.

Signature:

_.uniq(array, [isSorted], [iteratee])

union

Produces the union of multiple arrays. Returns a new array that contains unique values from all input arrays.

Signature:

_.union(*arrays)

intersection

Returns the intersection of multiple arrays; i.e., the elements that appear in all input arrays.

Signature:

_.intersection(*arrays)

difference

Returns the values from array that are not present in the other arrays.

Signature:

_.difference(array, *others)

zip

Merges together the values of several arrays, returning an array of arrays where each sub-array contains corresponding values from the input arrays.

Signature:

_.zip(*arrays)

Example:

_.zip(['a', 'b'], [1, 2], [true, false]); // => [['a', 1, true], ['b', 2, false]]

unzip

The inverse of zip. Takes an array of arrays and groups the elements based on their position.

Signature:

_.unzip(array)

range

Generates an array of numbers within a given range.

Signature:

_.range([start], stop, [step])

bindAll

Binds a number of methods to an object’s scope. Useful for creating functions that maintain context when called later.

Signature:

_.bindAll(object, *methodNames)

This section provides a concise overview. Consult the Underscore.js documentation for detailed examples and edge cases.

Functions

Underscore.js offers a suite of functions for working with and manipulating JavaScript functions themselves. These functions provide powerful tools for controlling function execution, managing context, and optimizing performance.

bind

Creates a function that, when called, has its this keyword set to a provided value, with a given sequence of arguments prepended to those provided during the actual call.

Signature:

_.bind(func, context, *args)

Example:

function greet(greeting) { console.log(greeting + ", " + this.name); }
var person = {name: "Alice"};
var boundGreet = _.bind(greet, person, "Hello");
boundGreet(); // Logs "Hello, Alice"

partial

Partially applies a function by filling in given arguments, and returning a new version of the function that accepts the remaining arguments.

Signature:

_.partial(func, *args)

Example:

function add(a, b, c) { return a + b + c; }
var add5 = _.partial(add, 5); // Partially apply the first argument as 5.
add5(2, 3); // Returns 10.

memoize

Creates a version of a function that will only call the original function once for any given set of arguments. Subsequent calls with the same arguments will return the cached result. Useful for expensive function calls.

Signature:

_.memoize(func, [hashFunction])

Example:

var expensiveFunction = _.memoize(function(n){ /* ...some expensive computation... */ return n*2; });
expensiveFunction(5); // Computes and caches the result.
expensiveFunction(5); // Returns cached result.

delay

Schedules a function to be called after a given delay in milliseconds.

Signature:

_.delay(func, wait, *args)

defer

Schedules a function to be called as soon as possible after the current call stack is cleared. Essentially a delay of 0ms.

Signature:

_.defer(func, *args)

throttle

Creates a throttled version of a function that only executes the function at most once every wait milliseconds. Useful for rate-limiting functions that are called frequently.

Signature:

_.throttle(func, wait, [options])

debounce

Creates a debounced version of a function. It will only execute the function after a specified delay, ignoring calls within that delay. Useful for handling events like window resizing or input changes.

Signature:

_.debounce(func, wait, [immediate])

once

Creates a version of a function that can only be called once. Subsequent calls will return the result of the first call.

Signature:

_.once(func)

after

Creates a function that will only be executed after being called n times.

Signature:

_.after(times, func)

compose

Creates a function that is the composition of a list of functions. Each function consumes the return value of the function that follows.

Signature:

_.compose(*functions)

wrap

Wraps the first function with another function, passing it as the first argument. Useful for adding pre- or post-processing to a function.

Signature:

_.wrap(func, wrapper)

This section provides a concise overview of Underscore.js’s function manipulation utilities. Consult the Underscore.js documentation for detailed examples and nuanced usage considerations.

Objects

Underscore.js provides a comprehensive set of functions for working with plain JavaScript objects. These functions simplify common object manipulations and enhance code readability and maintainability.

keys

Retrieve all the names of the object’s own enumerable properties.

Signature:

_.keys(object)

Example:

_.keys({one: 1, two: 2, three: 3}); // => ["one", "two", "three"]

values

Retrieve all the values of the object’s own enumerable properties.

Signature:

_.values(object)

Example:

_.values({one: 1, two: 2, three: 3}); // => [1, 2, 3]

pairs

Convert an object into a list of [key, value] pairs.

Signature:

_.pairs(object)

Example:

_.pairs({one: 1, two: 2}); // => [["one", 1], ["two", 2]]

invert

Returns a copy of the object where the keys and values are swapped.

Signature:

_.invert(object)

Example:

_.invert({a: 1, b: 2, c: 1}); // => {1: "c", 2: "b"}  (Note: last key wins in case of duplicates)

functions (alias: methods)

Retrieve the names of all the object’s own enumerable function properties.

Signature:

_.functions(object)

Example:

_.functions({a: function() {}, b: "value"}); // => ["a"]

extend (alias: assign)

Copy all of the properties in the source objects over to the destination object.

Signature:

_.extend(destination, *sources)

Example:

var dest = {a: 1};
_.extend(dest, {b: 2}, {c: 3}); // dest now equals {a: 1, b: 2, c: 3}

pick

Copy only the specified properties from the source object into a new object.

Signature:

_.pick(object, *keys)

Example:

_.pick({a: 1, b: 2, c: 3}, 'a', 'c'); // => {a: 1, c: 3}

omit

Create a new object omitting the specified properties from the source object.

Signature:

_.omit(object, *keys)

Example:

_.omit({a: 1, b: 2, c: 3}, 'a', 'c'); // => {b: 2}

defaults

Fill in undefined properties in object with values from the defaults objects, recursively.

Signature:

_.defaults(object, *defaults)

Example:

_.defaults({a: 1}, {a: 2, b: 3}); // => {a: 1, b: 3}

clone

Create a shallow-copied clone of the object.

Signature:

_.clone(object)

isEqual

Perform a deep comparison between two objects or values.

Signature:

_.isEqual(object, other)

isEmpty

Check if an object (or array) is empty.

Signature:

_.isEmpty(object)

isElement

Check if an object is a DOM element.

Signature:

_.isElement(object)

isArray, isObject, isArguments, isFunction, isString, isNumber, isDate, isBoolean, isUndefined, isNull

These functions are type-checking predicates, returning true if the object is of the specified type, and false otherwise. Their signatures are all:

_.isArray(object) // etc.

has

Check if an object has a given key.

Signature:

_.has(object, key)

This section provides a concise overview. Refer to the Underscore.js documentation for complete details and examples. Note that many of these functions operate on arrays as well as objects, leveraging Underscore’s flexible handling of collections.

Utility Functions

Underscore.js includes a set of general-purpose utility functions that don’t neatly fit into the other categories (Collections, Arrays, Objects, Functions). These functions are invaluable for various tasks throughout your JavaScript code.

identity

A function that returns its first argument. Useful as a default iteratee or when you need a no-op function.

Signature:

_.identity(value)

Example:

_.map([1, 2, 3], _.identity); // => [1, 2, 3]

constant

Creates a function that returns a particular value. Useful for creating functions that always return the same result, regardless of input.

Signature:

_.constant(value)

Example:

var five = _.constant(5);
five(); // => 5
five(10); // => 5

uniqueId

Generates a unique ID. The ID is a string, and subsequent calls generate incrementing IDs. Optionally, you can provide a prefix.

Signature:

_.uniqueId([prefix])

Example:

_.uniqueId(); // => "id1"
_.uniqueId('prefix-'); // => "prefix-id2"

escape

Escapes a string for insertion into HTML. Escapes <, >, ", ', and &.

Signature:

_.escape(string)

Example:

_.escape("<h1>Hello</h1>"); // => "&lt;h1&gt;Hello&lt;/h1&gt;"

unescape

The inverse of escape. Unescapes escaped HTML entities.

Signature:

_.unescape(string)

result

Call a method on a given object with some arguments, and return the result. If the method is not found, returns the property directly.

Signature:

_.result(object, property, *args)

Example:

var obj = {
  name: 'moe',
  greet: function(name){ return 'hi: ' + name; }
};
_.result(obj, 'name');      // => 'moe'
_.result(obj, 'greet', 'curly'); // => 'hi: curly'

template

Compiles a template string into a function that can be used to generate HTML. Uses a simple templating syntax (similar to ERB).

Signature:

_.template(templateString, [data], [settings])

Example:

var compiled = _.template("Hello, <%= name %>!");
compiled({name: "World"}); // => "Hello, World!"

mixin

Adds functions to the Underscore object, or an arbitrary object. Allows you to extend Underscore’s functionality or create your own utility libraries.

Signature:

_.mixin(object)

This section provides a concise overview. Consult the Underscore.js documentation for comprehensive details and examples of these versatile utility functions. Remember that template requires careful consideration of security if user-supplied data is involved, to avoid potential XSS vulnerabilities.

Chaining

Underscore.js supports method chaining, allowing you to string together multiple operations on a collection in a fluent and readable manner. This significantly improves code clarity and reduces the need for intermediate variables.

Chain Methods

Most Underscore.js collection methods (those that operate on arrays or objects) return a wrapped version of the collection. This wrapped object has its own methods, allowing you to chain additional operations. The methods available on the wrapped object are the same as those on the _ object itself, but they operate on the wrapped collection and return new wrapped objects, enabling further chaining. The chain is broken when you call a method that doesn’t return a wrapped object (e.g., _.value()).

_.chain()

Initiates a chain sequence. It takes a collection (array or object) as input and returns a wrapped object. This object has all the Underscore.js collection methods available for chaining.

Signature:

_.chain(obj)

Example:

var result = _( [1, 2, 3, 4, 5, 6] )
  .chain()
  .filter( function(num){ return num % 2 === 0; } )
  .map( function(num){ return num * 2; } )
  .value(); //Remember to call value() to get the final result

console.log(result); // => [4, 8, 12]

In this example, _.chain() creates a wrapped object. The .filter() and .map() methods operate on the wrapped object and return new wrapped objects, allowing the chain to continue. Finally, .value() unwraps the final result, returning a regular array.

_.value()

Terminates a chain sequence and returns the unwrapped result. It’s crucial to call _.value() at the end of a chain to obtain the final result of the chained operations. Without it, you’d only have a wrapped object, not the actual processed data.

Signature:

_.value() // Called on the wrapped object

Example: (same as the previous example, but highlighting .value())

var result = _( [1, 2, 3, 4, 5, 6] )
  .chain()
  .filter( function(num){ return num % 2 === 0; } )
  .map( function(num){ return num * 2; } )
  .value(); // <---  Here's the crucial _.value() call

console.log(result); // => [4, 8, 12]

Without the .value() call, result would be a wrapped Underscore object, not the array [4, 8, 12]. This is a common mistake when working with Underscore’s chaining functionality. Always remember to unwrap the result using .value().

This section explains the fundamentals of chaining in Underscore.js. Efficient use of chaining can significantly improve the elegance and readability of your code when performing multiple operations on collections. Remember that only methods that return wrapped objects can be chained.

Advanced Techniques and Best Practices

This section delves into more advanced aspects of using Underscore.js effectively, focusing on best practices and techniques to maximize its benefits.

Error Handling

Underscore.js itself doesn’t offer specific error-handling mechanisms beyond the standard JavaScript exceptions. However, you should incorporate robust error handling within your code when using Underscore.js functions. This is especially crucial when working with iterators and functions that might encounter unexpected input:

Performance Optimization

While Underscore.js is generally efficient, you can further optimize performance in your applications:

Integration with other libraries

Underscore.js works well with other JavaScript libraries. There’s no inherent conflict. However, be mindful of potential naming collisions. If another library uses the same names as Underscore functions, you might need to adjust your code (e.g., using aliases or a different naming scheme). Also, be aware that Underscore.js does not directly manipulate the DOM. If DOM manipulation is needed, you’ll typically use a library like jQuery in conjunction with Underscore.

Common Use Cases and Examples

Underscore.js shines in several common development scenarios:

Example: Transforming an array of objects:

var users = [
  {id: 1, name: "Alice", active: true},
  {id: 2, name: "Bob", active: false},
  {id: 3, name: "Charlie", active: true}
];

var activeUsers = _.chain(users)
  .filter(user => user.active)
  .map(user => user.name)
  .value();

console.log(activeUsers); // => ["Alice", "Charlie"]

This section provides guidance on leveraging Underscore.js effectively. By incorporating these best practices, you can build robust, maintainable, and efficient JavaScript applications. Remember to always consult the official Underscore.js documentation for the most up-to-date information.

Appendix

This appendix provides supplementary information to aid your understanding and use of Underscore.js.

Glossary of Terms

Frequently Asked Questions (FAQ)

Further Resources and Learning

This appendix provides supplemental material to enhance your Underscore.js experience. Remember to always refer to the official documentation for the most accurate and up-to-date information.