Underscore.js
Underscore.js is a lightweight JavaScript utility library that serves as a "belt" of functional programming helpers, providing over 100 functions for common tasks like array and object manipulation, without extending any built-in JavaScript objects or prototypes.[1]
Developed by Jeremy Ashkenas, it was first released on October 28, 2009, as version 0.1.0, drawing inspiration from functional paradigms in languages such as Ruby and Python to simplify data processing in client-side and server-side JavaScript environments.[2]
Key features include collection methods like _.each, _.map, _.filter, and _.reduce for iterating and transforming data; object utilities such as _.keys, _.pick, and _.extend for property handling; and additional tools for templating, debouncing, and deep equality checks (_.isEqual), all accessible via a concise underscore prefix (_).[1]
The library is open-source under the MIT License, maintained on GitHub with contributions from developers like Dmitry Baranovskiy and Kris Kowal, and emphasizes compatibility with ES3+ engines, supporting browsers including Chrome 26+, Firefox 11+, Internet Explorer 9+, Safari 8+, and Node.js 8+.[2]
It is frequently used alongside frameworks like Backbone.js (also created by Ashkenas) and jQuery to enhance productivity in web development, though many of its functions have been incorporated into modern ECMAScript standards, reducing its necessity in newer projects.[1]
As of July 2024, the latest stable version is 1.13.7, available via npm (npm install underscore) or CDN for easy integration.[3]
History
Origins and Initial Development
Underscore.js was created by Jeremy Ashkenas in October 2009 as a lightweight JavaScript utility library aimed at enhancing functional programming capabilities in the language.[2] Developed initially under the DocumentCloud project, it emerged during a period when JavaScript developers sought concise tools for handling collections and data manipulation without heavy frameworks.[4] Ashkenas, known for his work on CoffeeScript and Backbone.js, designed Underscore.js to fill gaps in native JavaScript by providing a minimal set of utilities that could be dropped into any project.[1]
The library drew inspiration from functional programming paradigms in languages like Ruby's Enumerable module, which offers methods for iterating and transforming collections, as well as from JavaScript libraries such as Prototype.js.[5] However, Underscore.js deliberately avoided extending native JavaScript objects—such as Array or Object prototypes—to prevent conflicts with other libraries or future ECMAScript updates, a common issue with Prototype.js.[2] This design choice prioritized compatibility and safety, allowing developers to use the library as a standalone underscore object (_) without altering the global namespace.[1]
The initial release, version 0.1.0, occurred on October 28, 2009, and focused on essential collection helpers including map, filter, and each to support common operations on arrays and objects.[1] Early goals emphasized providing "functional programming support" in a dependency-free manner, enabling immediate productivity on a blank HTML page while avoiding global pollution through object extensions.[5] This foundational approach laid the groundwork for Underscore.js's role as a versatile utility belt in JavaScript development.[2]
Key Releases and Maintenance
Underscore.js began its development with the release of version 0.1.0 on October 28, 2009, providing initial functional programming utilities for JavaScript.[1] The library progressed through early versions, reaching version 1.0.0 on March 18, 2010, which marked a significant milestone in stabilizing its core API for broader adoption.[6] During this period, version 0.4.0 introduced chaining capabilities, allowing methods to be composed sequentially on wrapped objects, while version 0.3.1 added enhancements to iteratee handling for more flexible callback processing in collection operations.
Subsequent stable releases built on this foundation, with version 1.8.3 released on April 2, 2015, incorporating support for ES6 features such as improved handling of iterables and native methods where available.[7] Version 1.13.0, released on April 9, 2021, extended compatibility by adding support for template literals in its templating system and introducing native ESM builds for modern module systems.[8] The most recent update, version 1.13.7 on July 24, 2024, focused on minor bug fixes, including a DataView-related issue, along with documentation improvements and security patches.[9] As of November 2025, no further releases have occurred.[3]
Maintenance of Underscore.js has been led by creator Jeremy Ashkenas through the official GitHub repository at jashkenas/underscore, with ongoing community contributions via pull requests and issues.[2] The project entered a phase of reduced activity after 2022, with limited commits and no major updates since the 2024 release, reflecting a mature but low-maintenance status amid the rise of native JavaScript features.[10]
In terms of compatibility, early versions targeted ES3 environments, supporting browsers like Internet Explorer 9 and above, as well as Node.js 0.8 and later.[1] Later iterations, particularly from version 1.13 onward, evolved to include modular ESM builds alongside traditional UMD and AMD formats, enabling seamless integration with contemporary bundlers and runtimes while preserving backward compatibility.[11]
Relationship to Lodash
Lodash emerged in 2012 as a fork of Underscore.js, developed by John-David Dalton to address perceived shortcomings in performance, API consistency across environments, and modularity.[12][13]
While Underscore.js adhered to its minimalist design philosophy, emphasizing a lightweight utility belt without extending native objects, Lodash introduced optimizations like faster array and object manipulations, additional utility functions, and a modular structure allowing selective imports.[12][14]
Lodash rapidly gained traction due to these enhancements, surpassing Underscore.js in popularity and adoption metrics by around 2015, as evidenced by discussions within the development community and growing npm download trends.[15][12]
In response to Lodash's advancements, Underscore.js incorporated select innovations, such as refinements to partial application functionality in version 1.8.0 and subsequent releases, improving argument handling and flexibility.[16][14] However, Underscore.js transitioned to maintenance mode with infrequent updates, leading to relatively declining usage in new projects compared to Lodash.
As of 2025, both libraries remain backward-compatible in most cases, enabling seamless migration, but Lodash is widely recommended for contemporary development owing to its ongoing active maintenance and superior performance characteristics.[12][17]
Overview
Purpose and Design Philosophy
Underscore.js serves as a lightweight JavaScript library offering over 100 functional programming utility functions to assist with everyday tasks, particularly data manipulation on arrays, objects, and collections.[1] Designed to enhance productivity in vanilla JavaScript environments, it provides these helpers without any external dependencies or the need for broader frameworks, allowing developers to integrate it seamlessly into projects of any scale.[1] This focus positions Underscore.js as an essential tool for writing efficient, modern code in browsers or Node.js, answering the core need for immediate utility when starting from a blank slate.[1]
The library's design philosophy revolves around the concept of a "utility belt" for functional programming, emphasizing concise syntax, readability, and portability across ES3-compatible JavaScript engines.[1] Key principles include avoiding extensions to built-in prototypes—such as Array or Object—to prevent conflicts with other libraries or native code, and promoting immutability by having most functions return new values rather than altering inputs in place.[1] With no runtime dependencies and a minimal footprint, Underscore.js prioritizes clean, declarative approaches over verbose imperative constructs, fostering code that is both maintainable and performant.[1]
Among its benefits, Underscore.js significantly reduces boilerplate for common operations, such as iterating over collections or transforming data, by encouraging declarative patterns like using map instead of manual for-loops, which improves code clarity and developer efficiency.[1] Nevertheless, it deliberately limits its scope to these utility functions, eschewing the comprehensive features of full frameworks to avoid unnecessary complexity and ensure broad compatibility without introducing bloat.[1]
Compatibility and Installation
Underscore.js is compatible with ECMAScript 3 (ES3) and later environments, supporting a range of browsers including Chrome 26 and later, Edge 13/18 and later, Firefox 11 and later, Internet Explorer 9 through 11, and Safari 8 and later.[1] It also runs on Node.js versions 8 through the latest LTS release, as well as Adobe ExtendScript environments, while retaining code for IE 8 compatibility without active support.[1]
Installation of Underscore.js can be performed via several methods to suit different project setups. The primary approach for Node.js-based projects is using NPM with the command npm install underscore, which installs version 1.13.7 as of the latest release.[3] For browser-based applications, it can be loaded directly via a script tag from a CDN such as jsDelivr, for example: <script src="https://cdn.jsdelivr.net/npm/[email protected]/underscore-umd-min.js"></script>.[1]
Import options in Underscore.js provide flexibility across module systems. The monolithic UMD build exposes the global _ object when loaded in browsers without a module loader.[1] For modern environments, it supports ESM imports via import _ from 'underscore-esm';, enabling tree-shaking and modular usage.[11] CommonJS modules are also supported with const _ = require('underscore');, making it compatible with older Node.js workflows.[1]
To avoid conflicts with other libraries that may use the global _ namespace, such as jQuery, Underscore.js includes a noConflict mode accessible via _.noConflict(), which returns the Underscore object and releases the global variable for reassignment.[1] This feature is available in the UMD build but not in ESM, AMD, or CommonJS contexts, where globals are not presumed.[1]
Core Concepts
The Underscore Object (_)
The Underscore object, conventionally denoted as _, functions as the primary global namespace for the Underscore.js library, encapsulating all its utility functions without extending native JavaScript prototypes. This design allows developers to access methods like _.map(array, fn), which transforms an array by applying a given function to each element and returns a new array with the results.[1] By centralizing utilities under _, the library maintains a clean, non-intrusive footprint in JavaScript environments.[2]
Beyond serving as a static namespace and accessor for these functions, _ plays several dynamic roles in Underscore.js. As a wrapper factory, invoking _(obj) creates a wrapped version of the input object, enabling subsequent method calls in an object-oriented manner.[1] It also acts as a placeholder argument in partial function applications, such as _.partial(fn, _, 2), where _ indicates a position to be filled later during invocation.[1] Additionally, _ provides customization hooks, exemplified by _.templateSettings, which allows users to modify default behaviors for features like string templating.[1]
Initialization of _ varies by environment to balance convenience and modularity. In browser-based applications, including the Underscore.js script via a <script> tag automatically exposes _ in the global scope, making it immediately available without further setup.[1] In Node.js, however, the library is installed via npm and explicitly required within modules, such as var _ = require('underscore');, to integrate it locally.[2]
To prevent global namespace conflicts and encourage maintainable code, best practices recommend requiring Underscore.js explicitly in modular contexts, such as CommonJS or ES modules, rather than depending on the global _ variable.[1] This approach aligns with the library's emphasis on lightweight, dependency-managed integration.[2]
Chaining and Wrapping
Underscore.js provides a chaining mechanism through wrapped objects, allowing developers to invoke multiple utility functions sequentially in a fluent style without assigning intermediate results to variables. The primary entry point is the _.chain(obj) function, which returns a wrapped version of the input object; subsequent method calls on this wrapper, such as .map() or .filter(), return further wrapped objects rather than raw values, maintaining the chain until explicitly terminated.[18] Alternatively, invoking Underscore as a function with _(obj) also produces a wrapped object that supports chaining, and the chain concludes by calling .value() to unwrap and retrieve the final result.[19] This wrapping leverages the Underscore object (_) as a foundational accessor that holds altered versions of its utility functions, enabling object-oriented style invocations.[20]
For example, to double numbers in an array and filter those greater than 3, one can write:
javascript
_.chain([1, 2, 3])
.[map](/page/Map)(x => x * 2)
.[filter](/page/Filter)(x => x > 3)
.[value](/page/Value)();
_.chain([1, 2, 3])
.[map](/page/Map)(x => x * 2)
.[filter](/page/Filter)(x => x > 3)
.[value](/page/Value)();
This yields [4, 6], demonstrating how chaining composes operations like mapping and filtering seamlessly.[18] A more complex case involves processing text, such as splitting lyrics into words, flattening the result, and counting occurrences:
javascript
_.[chain](/page/Chain)(lyrics)
.[map](/page/Map)(line => line.words.split(' '))
.flatten()
.reduce((counts, word) => {
counts[word] = (counts[word] || 0) + 1;
return counts;
}, {})
.[value](/page/Value)();
_.[chain](/page/Chain)(lyrics)
.[map](/page/Map)(line => line.words.split(' '))
.flatten()
.reduce((counts, word) => {
counts[word] = (counts[word] || 0) + 1;
return counts;
}, {})
.[value](/page/Value)();
This produces a word count object, illustrating chaining's utility for multi-step data transformations.[18]
The advantages of this approach include improved code readability and reduced need for temporary variables, as operations flow linearly rather than nesting or requiring multiple statements.[18] However, Underscore's chaining is not lazy by default; each method in the chain evaluates immediately upon invocation, rather than deferring computation until the final .value() call, which can impact performance for large datasets.[21] In modern JavaScript environments, with native array methods supporting direct chaining (e.g., [1, 2, 3].map(x => x * 2).filter(x => x > 3)), Underscore's wrapping feature has become less essential for simpler, dependency-free code.[22]
Iteratees and Shorthands
Standard Iteratee Usage
In Underscore.js, an iteratee serves as a callback function passed to various collection-processing methods, such as _.map or _.filter, to transform, test, or otherwise operate on each element of the input collection.[23] These methods invoke the iteratee for every value in the collection, providing it with three arguments: the current value (element), its index (for arrays) or key (for objects), and a reference to the entire collection.[24] This standardized signature enables consistent behavior across functions, allowing developers to access contextual information during iteration without additional setup.[25]
Underscore.js supports convenient shorthands for common iteratee patterns, reducing the need for explicit function definitions. A string shorthand, such as 'name', is interpreted via _.property to extract the specified property from each object in the collection; for example, _.map(users, 'name') returns an array of the name properties from a collection of user objects.[23] Similarly, an object shorthand acts as a matcher via _.matcher, enabling attribute-based filtering; _.filter(items, {active: true}) selects only those items where the active property equals true.[26] These notations simplify code for property access and conditional matching, promoting readability in functional-style programming.[25]
When an iteratee argument is null or undefined, Underscore.js defaults to the identity function, which simply returns its input value unchanged, effectively passing elements through without modification.[25] This behavior ensures graceful handling in cases where no transformation is needed, avoiding errors and aligning with the library's emphasis on safe, predictable operations.[27]
Customization and Overrides
The _.iteratee function in Underscore.js acts as the central resolver for transforming shorthand notations into executable callback functions, which are then applied by various collection methods such as _.map, _.filter, and _.sortBy. It accepts a value parameter representing the potential iteratee and an optional context for binding, defaulting to handling common types: functions are returned unchanged, objects are converted to matchers via _.matcher for partial equality checks, strings are transformed into property accessors via _.property for simple attribute extraction (treating the string as a literal key), and arrays are converted to property accessors via _.property using the array elements as a path for nested access. This resolution process ensures concise syntax for common operations while maintaining flexibility.[28][29]
For nested properties, arrays can be used to specify paths; for example, passing ['user', 'name'] as the value resolves to a function that retrieves the name property from the user object within each element. This is facilitated internally by the _.toPath utility, which normalizes arrays into iterable segments for deep traversal (strings are wrapped as single-element arrays without splitting on dots). Such path handling streamlines operations on hierarchical data structures like JSON objects or DOM elements.[28][30]
To extend or alter this default behavior, developers can override _.iteratee entirely by reassigning it to a custom function, allowing the addition of new shorthand styles tailored to specific needs. For instance, a custom implementation could recognize regular expressions as input values, converting them into predicate functions for pattern-based filtering, thus supporting shorthand like /^prefix/ in place of verbose regex tests. This overriding approach provides a clean entry point for personalization without modifying core library code.[28][29]
Further customization often involves overriding the supporting _.toPath function, which powers path resolution in _.iteratee and related utilities. By replacing _.toPath, users can enable advanced string path shorthands compatible with libraries like Lodash, such as interpreting 'a.0.b' to access the b property of the first array element in a, thereby extending Underscore.js's matching capabilities for mixed array-object data. This integration facilitates hybrid usage scenarios, where Lodash's more permissive path syntax enhances Underscore's lighter footprint.[30]
These customization options are particularly valuable for adapting Underscore.js to domain-specific requirements or optimizing performance in constrained environments. For example, in applications with complex data models—such as those involving framework-specific props or API responses—custom shorthands can reduce boilerplate and improve readability, while tuned iteratee logic might cache resolutions for repeated operations on large collections. Overall, such overrides empower developers to evolve the library's utility layer without forking the source.[28][29]
Collection Functions
Iteration and Mapping
Underscore.js provides several functions for iterating over collections and mapping transformations, enabling efficient traversal and modification of arrays and objects without mutating the originals. These utilities emphasize functional programming principles, allowing developers to process data declaratively. The core iteration function, _.each, performs side-effect-based operations, while _.map focuses on producing transformed outputs. Additional helpers like _.invoke and _.pluck streamline common patterns in collection manipulation.[27]
The _.each function, also aliased as _.forEach, iterates over elements in a list, invoking an iteratee function for each without returning a new collection. Its signature is _.each(list, iteratee, [context]), where the iteratee receives three arguments: the element (or value for objects), the index (or key), and the list itself. For arrays and array-like objects, iteration proceeds from left to right; for objects, it yields values with their corresponding keys. The function returns the original list to support chaining and handles array-like objects such as NodeLists or arguments, while avoiding plain objects with numeric length properties to prevent unintended behavior. Unlike native loops, it cannot be broken early; for early termination, alternatives like _.find are recommended. For example:
javascript
_.each([1, 2, 3], function(num, [index](/page/Index)) {
console.[log](/page/Log)(num); // Logs 1, 2, 3
});
_.each({one: 1, two: 2}, function(value, key) {
console.[log](/page/Log)(key + ': ' + value); // Logs 'one: 1', 'two: 2'
});
_.each([1, 2, 3], function(num, [index](/page/Index)) {
console.[log](/page/Log)(num); // Logs 1, 2, 3
});
_.each({one: 1, two: 2}, function(value, key) {
console.[log](/page/Log)(key + ': ' + value); // Logs 'one: 1', 'two: 2'
});
This function supports sparse arrays through duck-typing, ensuring consistent iteration even with undefined elements.[24]
In contrast, _.map, aliased as _.collect, transforms a collection by applying an iteratee to each element and returning a new array of results. Its signature mirrors _.each: _.map([list](/page/List), iteratee, [context]), with the iteratee again receiving value, index/key, and list. When applied to objects, it treats them as collections of [key, value] pairs but returns an array of transformed values in the order of enumeration. The resulting array preserves the length of the input, including undefined values for sparse arrays. This makes _.map ideal for one-to-one transformations, such as scaling values or extracting derived data. For instance:
javascript
_.map([1, 2, 3], function(num) {
return num * 3;
}); // Returns [3, 6, 9]
_.map({a: 1, b: 2}, function(value, key) {
return value * 2;
}); // Returns [2, 4], assuming modern JS insertion order
_.map([1, 2, 3], function(num) {
return num * 3;
}); // Returns [3, 6, 9]
_.map({a: 1, b: 2}, function(value, key) {
return value * 2;
}); // Returns [2, 4], assuming modern JS insertion order
Iterate shorthands, such as _.property('key'), can simplify these operations by providing property-extracting functions.[23]
The _.invoke function applies a specified method to each element in a collection, forwarding any additional arguments to that method and collecting the results into a new array. Its signature is _.invoke(list, methodName, *arguments), where methodName can be a string or a path to a method. It operates primarily on arrays but extends to objects with invocable properties, ensuring the method exists on each element to avoid errors. This is particularly useful for uniform operations across similar objects, like sorting arrays within an array. An example illustrates its utility:
javascript
_.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
// Returns [[1, 5, 7], [1, 2, 3]]
_.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
// Returns [[1, 5, 7], [1, 2, 3]]
Sparse arrays are handled consistently, though the focus remains on the invoked method's behavior.[31]
Finally, _.pluck offers a concise way to extract a single property from each object in a list, equivalent to mapping with an iteratee that accesses that property. Its signature is _.pluck(list, propertyName), returning an array of the specified values. Designed for arrays of objects, it leverages iteratee shorthands internally for efficiency. For example:
javascript
var stooges = [{name: 'Moe', age: 40}, {name: 'Larry', age: 50}];
_.pluck(stooges, 'name'); // Returns ['Moe', 'Larry']
var stooges = [{name: 'Moe', age: 40}, {name: 'Larry', age: 50}];
_.pluck(stooges, 'name'); // Returns ['Moe', 'Larry']
This function maintains the input's structure for sparse cases, providing a streamlined alternative to explicit _.map calls.[32]
Reduction and Aggregation
Underscore.js provides several functions for reducing collections—such as arrays and objects—into single values or aggregated structures, enabling efficient summarization and transformation of data. These utilities draw from functional programming paradigms, allowing developers to accumulate results through iteratees or compute extrema and groupings without mutating the original collection.[1]
The core reduction function, _.reduce, also known as inject or foldl, iterates over a collection from left to right, applying an iteratee function to each element and an accumulator (memo) to produce a single output value. Its signature is _.reduce(list, iteratee, [memo], [context]), where list is the collection, iteratee is a function invoked as (memo, value, indexOrKey, list) returning the updated memo, memo is the optional initial accumulator (defaulting to the first element if omitted), and context binds this for the iteratee. For example, summing an array uses _.reduce([1, 2, 3], (memo, num) => memo + num, 0), yielding 6.[33] If the collection is empty and no memo is provided, the function returns undefined.[33]
A right-to-left variant, _.reduceRight (aliased as foldr), performs the same accumulation but processes the collection in reverse order, useful for operations like flattening nested arrays from the end. Its signature mirrors _.reduce: _.reduceRight(list, iteratee, [memo], [context]). An example is _.reduceRight([[0, 1], [2, 3]], (a, b) => a.concat(b), []), resulting in [2, 3, 0, 1]. Like its counterpart, it returns undefined for empty collections without a memo.[34]
For finding extrema, _.max and _.min return the maximum or minimum value in a collection, optionally using an iteratee for custom comparisons. The signature for both is _.max(list, [iteratee], [context]) and _.min(list, [iteratee], [context]), where iteratee transforms elements (e.g., by property) before numeric comparison via the < operator. For instance, _.max([{age: 40}, {age: 60}], 'age') returns {age: 60}, while _.min([10, 2, 1000]) yields 2. Empty collections return -Infinity for _.max and Infinity for _.min, and these functions reliably compare only numbers.[35][36]
Aggregation into grouped structures is handled by _.groupBy, _.indexBy, and _.countBy, which partition collections based on iteratee results. _.groupBy(list, iteratee, [context]) creates an object with keys as iteratee outputs and values as arrays of matching elements; for example, _.groupBy(users, 'age') produces an object grouping user objects by age property. _.indexBy(list, iteratee, [context]) builds a similar object but indexes elements by unique keys, overwriting duplicates with the last occurrence, as in _.indexBy([{age: 40}, {age: 50}], 'age') yielding {"40": {age: 40}, "50": {age: 50}}. _.countBy(list, iteratee, [context]) tallies frequencies, returning an object of counts; e.g., _.countBy([1, 2, 3], num => num % 2 === 0 ? 'even' : 'odd') gives {odd: 2, even: 1}. All three return empty objects for empty collections.[37][38][39]
Finally, _.size(obj) simply returns the count of values in an array, object, or string-like collection, equivalent to length for arrays but handling objects via enumeration. Examples include _.size([1, 2, 3]) returning 3 and _.size({one: 1, two: 2}) returning 2; it yields 0 for empty collections.[40]
Array Functions
Selection and Extraction
The selection and extraction functions in Underscore.js provide utilities for retrieving subsets of arrays based on position or for cleaning and reshaping array contents by removing unwanted elements or flattening nested structures. These functions operate directly on arrays, enabling efficient positional slicing without mutating the original data. They complement broader collection iteration methods by focusing on array-specific manipulations for initial setup or data preparation tasks.[41]
The _.first function, also aliased as _.head, retrieves the first element of an array or the first n elements if a positive integer n is provided as the second argument; if n exceeds the array length, it returns the entire array. For example, _.first([1, 2, 3, 4], 2) returns [1, 2]. This is useful for extracting prefixes in data processing workflows.[41]
Similarly, _.last returns the last element or the last n elements of an array, with behavior mirroring _.first for oversized n. It supports the same aliasing and edge-case handling as its counterpart.[42]
For excluding elements from the ends, _.initial returns all elements except the last one, or excludes the last n elements if specified, defaulting to n=1. This pairs effectively with _.last for balanced extractions. Meanwhile, _.rest, aliased as _.tail, returns elements starting from a specified index (defaulting to index 1, thus skipping the first element); it is particularly handy for dropping prefixes. Both functions preserve the original array's order and handle empty arrays by returning empty arrays.[43][44]
Extraction also includes reshaping operations like _.flatten, which recursively flattens nested arrays into a single-level array unless the optional depth parameter is provided: true or 1 for only the outermost level, a positive integer n for n levels, or omitted/Infinity/ false for full flattening (default). For instance, _.flatten([[1], [2, 3]]) yields [1, 2, 3]. This function is essential for normalizing irregularly nested data structures common in JSON parsing or API responses.[45]
Complementing flattening, _.compact creates a shallow copy of the array with all falsy values (such as null, undefined, 0, false, NaN, or empty strings) removed, retaining only truthy elements. An example is _.compact([0, 1, false, 2, '', 3]), which returns [1, 2, 3]. It applies a simple truthiness check without further iteration, making it efficient for quick filtering in array pipelines.[46]
Set Operations
Underscore.js provides several array functions that perform set-like operations, enabling developers to compute unions, intersections, differences, and other relational manipulations on arrays of primitive values or objects. These functions treat arrays as unordered collections where equality is determined by strict equality (===), facilitating efficient handling of duplicates and overlaps without requiring native Set support in older JavaScript environments.[1]
The union function computes the union of two or more arrays by returning a new array containing all unique elements present in any of the input arrays, preserving the order of first occurrence. Its signature is _.union(*arrays), where *arrays denotes a variable number of input arrays. For instance, _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]) yields [1, 2, 3, 101, 10]. This operation is useful for merging datasets while eliminating redundancies.[47]
In contrast, the intersection function identifies common elements across all input arrays, returning only those values that appear in every array provided. It uses the signature _.intersection(*arrays) and performs strict equality checks. An example is _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]), which returns [1, 2]. This is particularly handy for finding shared items in multiple lists, such as overlapping categories in data processing.[48]
The difference function extracts elements from the first array that are absent from any of the subsequent arrays, effectively performing a set subtraction. Its signature is _.difference(array, *others), with the primary array followed by one or more arrays to exclude from. For example, _.difference([1, 2, 3, 4, 5], [5, 2, 10]) results in [1, 3, 4]. This aids in filtering out unwanted values based on external criteria.[49]
To remove duplicates within a single array, Underscore.js offers the uniq (or unique) function, which produces a duplicate-free version by retaining only the first occurrence of each value. The signature is _.uniq(array, [isSorted], [iteratee], [context]), where isSorted (a boolean) optimizes performance for pre-sorted arrays using a faster linear algorithm, and iteratee allows custom uniqueness based on a transformation function—such as extracting properties from objects. For a basic case, _.uniq([1, 2, 1, 4, 1, 3]) returns [1, 2, 4, 3]. When using an iteratee, developers can define custom equality, for example, by passing a function to compare object attributes.[50]
Complementing uniqueness, the without function creates a copy of an array with all instances of specified values removed, regardless of their position or multiplicity. It takes the signature _.without(array, *values), where *values are the elements to exclude. An example is _.without([1, 2, 1, 0, 3, 1, 4], 0, 1), yielding [2, 3, 4]. This is efficient for simple value-based filtering without needing an iteratee.[51]
Finally, the zip function transposes multiple arrays by grouping elements at corresponding indices into new arrays, simulating a matrix transposition for coordinated data sources. Its signature is _.zip(*arrays), accepting a variable number of arrays of equal or varying lengths (shorter arrays are padded with undefined). For instance, _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]) produces [["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]. This is valuable for restructuring tabular data into row-oriented formats.[52]
Object Functions
Inspection and Manipulation
Underscore.js provides a suite of utility functions for inspecting the structure and contents of objects, as well as manipulating them through copying, filtering, and subsetting operations. These functions enable developers to retrieve property details, check for emptiness or presence of keys, and create modified copies without altering the originals, promoting functional programming patterns in JavaScript. All operations are designed to work with plain objects, handling enumerable own properties while respecting JavaScript's prototypal inheritance.[1]
The inspection functions begin with _.keys(object), which returns an array of the object's own enumerable property names, ordered as they appear in the object's property enumeration. For instance, _.keys({one: 1, two: 2, three: 3}) yields ["one", "two", "three"]. Complementing this, _.values(object) extracts an array of the corresponding values in the same order, such as [1, 2, 3] for the example above. Similarly, _.pairs(object) transforms the object into an array of key-value pairs, returning [["one", 1], ["two", 2], ["three", 3]]. These functions facilitate easy iteration and transformation of object data into array forms for further processing.[53][54][55]
For property presence checks, _.has(object, key) determines whether the object has an own property with the specified key, returning a boolean. This is particularly useful for avoiding errors when accessing potentially undefined properties, as in _.has({a: 1}, "a") which returns true, while _.has({a: 1}, "b") returns false. Additionally, _.isEmpty(object) tests if the object has no enumerable own properties, returning true for an empty object {} and false otherwise. These utilities support robust object validation in codebases handling dynamic data structures.[56][57]
Manipulation functions focus on creating new objects by extending, cloning, or selecting subsets. _.extend(destination, *sources) performs a shallow copy of properties from one or more source objects into the destination, modifying and returning it; for example, _.extend({name: 'moe'}, {age: 50}) results in {name: 'moe', age: 50}. Properties from later sources override earlier ones if keys conflict. The variant _.assign (or _.extendOwn) limits copying to own properties, excluding inherited ones from prototypes. Both are shallow operations, meaning nested objects are copied by reference rather than deeply cloned. For a standalone shallow clone, _.clone(object) creates a new object with the same own properties, as in _.clone({name: 'moe', age: 30}) yielding {name: 'moe', age: 30}. Underscore.js does not provide built-in deep cloning, requiring external libraries for recursive copies.[58][59][60]
To create filtered copies, _.pick(object, *keys) returns a new object containing only the specified keys' properties, supporting string keys, arrays, or predicates; _.pick({name: 'moe', age: 50, user: 'moe'}, 'name', 'age') produces {name: 'moe', age: 50}. Conversely, _.omit(object, *keys) excludes the specified keys, yielding {user: 'moe'} in the same example. These functions are invaluable for data sanitization, such as extracting user profiles or removing sensitive fields, while preserving the original object intact.[61][62]
Type Assertions
Underscore.js provides a suite of utility functions for asserting and validating the types of JavaScript objects, offering more reliable alternatives to native methods like typeof or instanceof, which often fall short in handling edge cases such as arrays or NaN values. These functions, prefixed with is, return booleans and are designed for defensive programming in functional-style code, enabling developers to check object categories before operations like iteration or manipulation. They are particularly useful in environments where type coercion or prototype pollution might occur, ensuring predictable behavior across browsers.[63]
The core type-checking functions include _.isArray(object), which determines if the input is an array, returning true for [] unlike the native typeof operator that yields 'object'. Similarly, _.isObject(value) verifies if the value is a non-primitive object, encompassing plain objects {}, arrays, and functions, but excluding null. For primitives, _.isNumber(object) confirms numeric types including NaN, _.isString(object) checks strings, _.isFunction(object) identifies functions, _.isDate(object) validates Date instances, _.isRegExp(object) detects regular expressions, and _.isNaN(object) specifically tests for NaN without falsely flagging undefined as the native isNaN does. Additional specialized checks cover _.isElement(object) for DOM nodes and _.isArguments(object) for argument objects from function calls.[63][64][65][66][67][68][69][70][71][72]
These functions employ optimized internal checks, such as property enumeration or constructor verification, to outperform generic native approaches in performance-critical code. For instance:
javascript
_.isArray([1, 2, 3]); // true
_.isObject({}); // true
_.isFunction(alert); // true
_.isNumber([NaN](/page/NaN)); // true
_.isString("moe"); // true
_.isArray([1, 2, 3]); // true
_.isObject({}); // true
_.isFunction(alert); // true
_.isNumber([NaN](/page/NaN)); // true
_.isString("moe"); // true
They were incrementally added across Underscore.js versions, with _.isDate and _.isNaN introduced in v0.4.7, _.isRegExp in v0.5.0, and _.isArguments in v0.5.1, enhancing compatibility for deep equality and collection operations.[63][64]
Beyond basic type checks, _.isEqual(a, b) performs a deep equality comparison between two values, recursively traversing nested structures like objects, arrays, and primitives while respecting types such as RegExp patterns added in v0.5.0. It handles circular references and non-enumerable properties, returning true only if all corresponding elements match in value and type, unlike shallow equality with ===. This makes it invaluable for testing data integrity in functional pipelines or state comparisons.
Example usage:
javascript
_.isEqual({a: 1}, {a: 1}); // true
_.isEqual([1, {b: 2}], [1, {b: 2}]); // true
_.isEqual(/moe/, /moe/); // true (since v0.5.0)
_.isEqual(NaN, NaN); // true
_.isEqual({a: 1}, {a: 1}); // true
_.isEqual([1, {b: 2}], [1, {b: 2}]); // true
_.isEqual(/moe/, /moe/); // true (since v0.5.0)
_.isEqual(NaN, NaN); // true
The function short-circuits on mismatched types or lengths, providing efficient validation for complex data.[73]
Complementing these, _.has(object, key) safely checks if an object possesses a specific own property, using a protected hasOwnProperty call to avoid issues with overridden prototypes. It returns true for direct properties but false for inherited ones, aiding in object inspection prior to access.
javascript
_.has({a: 1, b: 2}, "b"); // true
_.has({a: 1}, "toString"); // false (inherited)
_.has({a: 1, b: 2}, "b"); // true
_.has({a: 1}, "toString"); // false (inherited)
This utility integrates seamlessly with type assertions for robust property validation in Underscore.js workflows.[56]
Function Utilities
Binding and Partial Application
Underscore.js provides utilities for function binding and partial application, enabling developers to create specialized function instances by fixing the execution context or pre-filling arguments. These features draw from functional programming concepts, allowing for more flexible and reusable code in JavaScript environments where the this keyword and argument passing can be unpredictable.[1]
The _.bind function creates a new function that, when invoked, has its this keyword set to a specified object, while also supporting partial application of initial arguments. Its signature is _.bind([function](/page/Function), object, *arguments), where the first parameter is the function to bind, the second is the context object, and subsequent optional arguments are pre-applied. This is particularly useful in event handlers or callbacks where maintaining object context is essential. For example:
javascript
var func = [function](/page/Function)(greeting) { return greeting + ': ' + this.name; };
func = _.bind(func, {name: 'moe'}, 'hi');
func();
=> 'hi: moe'
var func = [function](/page/Function)(greeting) { return greeting + ': ' + this.name; };
func = _.bind(func, {name: 'moe'}, 'hi');
func();
=> 'hi: moe'
In this case, the bound function ensures this.name resolves to 'moe' regardless of the calling context.[74]
Complementing binding, _.partial facilitates partial application by returning a new function with some arguments already supplied, without altering the this context. The signature is _.partial([function](/page/Function), *arguments), where placeholders denoted by the _ symbol allow later arguments to be inserted at runtime. This promotes currying-like behavior for composing functions with fixed parameters. An illustrative example is:
javascript
var subtract = function(a, b) { return b - a; };
sub5 = _.partial(subtract, 5);
sub5(20);
=> 15
subFrom20 = _.partial(subtract, _, 20);
subFrom20(5);
=> 15
var subtract = function(a, b) { return b - a; };
sub5 = _.partial(subtract, 5);
sub5(20);
=> 15
subFrom20 = _.partial(subtract, _, 20);
subFrom20(5);
=> 15
Here, sub5 pre-fills the first argument as 5, while subFrom20 uses _ to defer the first argument, enabling flexible invocation.[75]
For object-oriented patterns, _.bindAll binds multiple methods of an object to that object itself, preventing loss of context in asynchronous or event-driven code. Its signature is _.bindAll(object, *methodNames), taking the target object and a variable number of method names as strings. The function modifies the object in place by replacing the specified methods with bound versions. A practical usage in a view object might look like:
javascript
var buttonView = {
label: 'underscore',
onClick: function() { alert('clicked: ' + this.label); },
onHover: function() { console.log('hovering: ' + this.label); }
};
_.bindAll(buttonView, 'onClick', 'onHover');
jQuery('#underscore_button').on('click', buttonView.onClick);
var buttonView = {
label: 'underscore',
onClick: function() { alert('clicked: ' + this.label); },
onHover: function() { console.log('hovering: ' + this.label); }
};
_.bindAll(buttonView, 'onClick', 'onHover');
jQuery('#underscore_button').on('click', buttonView.onClick);
This ensures that this.label remains accessible within the event handlers, avoiding common pitfalls in JavaScript callbacks.[76]
Timing and Composition
Underscore.js provides several utilities for controlling the timing of function execution, which are particularly useful in handling frequent events such as user interactions or asynchronous operations. The _.debounce function creates a debounced version of a provided function, postponing its execution until after a specified wait period has elapsed since the last invocation, thereby preventing excessive calls during rapid successive triggers.[77] For instance, it can be applied to a search function to delay execution until the user stops typing: var debouncedSearch = _.debounce(performSearch, 300);, where performSearch is only invoked 300 milliseconds after the last keypress.[77] The function accepts a wait duration in milliseconds and an optional immediate boolean to execute on the leading edge instead of the trailing edge.[77]
Complementing debounce, the _.throttle function limits a function to execute at most once every specified interval, ensuring periodic invocation even amid continuous calls.[78] This is ideal for scenarios like scroll event handling, as in var throttledUpdate = _.throttle(updateUI, 100); $(window).scroll(throttledUpdate);, which updates the interface no more than every 100 milliseconds.[78] Parameters include the target function, wait time, and an optional options object to customize leading or trailing edge behavior, with a .cancel() method available to halt pending executions.[78] Additionally, _.once wraps a function to ensure it runs only once, caching and returning the result for subsequent calls, which is commonly used for one-time initialization tasks like var setup = _.once(initializeApp); setup();.[79]
For composition, Underscore.js offers tools to chain or modify functions, facilitating modular code design akin to middleware patterns. The _.after utility delays a function's execution until it has been invoked a specified number of times, useful for coordinating asynchronous callbacks: var finalRender = _.after(collection.length, renderResults); collection.forEach(item => asyncProcess(item, finalRender));.[80] Here, renderResults only proceeds after all items in collection complete their processing.[80] The _.compose function builds a pipeline by applying multiple functions from right to left, returning a new function that chains them sequentially: var process = _.compose(formatOutput, validateInput); process(data); applies validateInput first, then formatOutput.[81] An example is var compute = _.compose(square, addOne); compute(2);, which yields 9 by adding 1 to 2 and then squaring the result.[81]
The _.wrap function enables middleware-like wrapping by enclosing a target function within a wrapper, passing the original as the first argument to allow pre- and post-execution logic.[82] For example, var loggedHello = _.wrap(sayHello, function(originalFn, name) { console.log('Before'); var result = originalFn(name); console.log('After'); return result; }); adds logging around sayHello invocations.[82] This approach supports conditional execution or argument adjustment without altering the core function.[82]