Fact-checked by Grok 2 weeks ago

Underscore.js

Underscore.js is a lightweight utility library that serves as a "belt" of helpers, providing over 100 functions for common tasks like array and object manipulation, without extending any built-in objects or prototypes. 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 and to simplify data processing in client-side and server-side environments. Key features include collection methods like _.each, _.map, _.filter, and _.reduce for iterating and transforming ; 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 (_). The library is open-source under the , maintained on with contributions from developers like Dmitry Baranovskiy and Kris Kowal, and emphasizes compatibility with ES3+ engines, supporting browsers including Chrome 26+, 11+, +, 8+, and 8+. It is frequently used alongside frameworks like (also created by Ashkenas) and to enhance productivity in , though many of its functions have been incorporated into modern standards, reducing its necessity in newer projects. As of July 2024, the latest stable version is 1.13.7, available via (npm install underscore) or CDN for easy integration.

History

Origins and Initial Development

Underscore.js was created by Jeremy Ashkenas in October 2009 as a lightweight utility library aimed at enhancing capabilities in the language. 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. Ashkenas, known for his work on and , designed Underscore.js to fill gaps in native by providing a minimal set of utilities that could be dropped into any project. 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 libraries such as Prototype.js. However, Underscore.js deliberately avoided extending native objects—such as or Object prototypes—to prevent conflicts with other libraries or future updates, a common issue with Prototype.js. This design choice prioritized compatibility and safety, allowing developers to use the library as a standalone underscore object (_) without altering the global namespace. The initial release, version 0.1.0, occurred on , 2009, and focused on essential collection helpers including map, filter, and each to support common operations on arrays and objects. Early goals emphasized providing " support" in a dependency-free manner, enabling immediate productivity on a blank page while avoiding global pollution through object extensions. This foundational approach laid the groundwork for .js's role as a versatile utility belt in development.

Key Releases and Maintenance

Underscore.js began its development with the release of version 0.1.0 on October 28, 2009, providing initial utilities for . The library progressed through early versions, reaching version 1.0.0 on March 18, 2010, which marked a significant milestone in stabilizing its core for broader adoption. During this period, version 0.4.0 introduced 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. 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. 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. As of 2025, no further releases have occurred. Maintenance of Underscore.js has been led by creator Jeremy Ashkenas through the official repository at jashkenas/underscore, with ongoing community contributions via pull requests and issues. 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 features. In terms of compatibility, early versions targeted ES3 environments, supporting browsers like and above, as well as 0.8 and later. Later iterations, particularly from version 1.13 onward, evolved to include modular ESM builds alongside traditional UMD and formats, enabling seamless integration with contemporary bundlers and runtimes while preserving .

Relationship to

Lodash emerged in 2012 as a of Underscore.js, developed by John-David to address perceived shortcomings in performance, consistency across environments, and modularity. While Underscore.js adhered to its minimalist design philosophy, emphasizing a lightweight utility belt without extending native objects, introduced optimizations like faster array and object manipulations, additional utility functions, and a modular structure allowing selective imports. 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. 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. However, Underscore.js transitioned to 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.

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. 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. 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. The library's design philosophy revolves around the concept of a "utility belt" for , emphasizing concise syntax, readability, and portability across ES3-compatible engines. Key principles include avoiding extensions to built-in prototypes—such as 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. 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. 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. 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.

Compatibility and Installation

Underscore.js is compatible with 3 (ES3) and later environments, supporting a range of browsers including 26 and later, 13/18 and later, Firefox 11 and later, through 11, and Safari 8 and later. It also runs on versions 8 through the latest LTS release, as well as ExtendScript environments, while retaining code for 8 compatibility without active support. 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 with the command npm install underscore, which installs version 1.13.7 as of the latest release. For browser-based applications, it can be loaded directly via a script tag from a CDN such as , for example: <script src="https://cdn.jsdelivr.net/npm/[email protected]/underscore-umd-min.js"></script>. 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. For modern environments, it supports ESM imports via import _ from 'underscore-esm';, enabling tree-shaking and modular usage. CommonJS modules are also supported with const _ = require('underscore');, making it compatible with older Node.js workflows. To avoid conflicts with other libraries that may use the global _ namespace, such as , .js includes a noConflict mode accessible via _.noConflict(), which returns the Underscore object and releases the global variable for reassignment. This feature is available in the UMD build but not in ESM, , or contexts, where globals are not presumed.

Core Concepts

The Underscore Object (_)

The Underscore object, conventionally denoted as _, functions as the primary global 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 by applying a given to each element and returns a new array with the results. By centralizing utilities under _, the maintains a clean, non-intrusive footprint in environments. 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. 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. Additionally, _ provides customization hooks, exemplified by _.templateSettings, which allows users to modify default behaviors for features like string templating. 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. In Node.js, however, the library is installed via npm and explicitly required within modules, such as var _ = require('underscore');, to integrate it locally. To prevent global namespace conflicts and encourage maintainable code, best practices recommend requiring explicitly in modular contexts, such as or ES modules, rather than depending on the global _ variable. This approach aligns with the library's emphasis on lightweight, dependency-managed integration.

Chaining and Wrapping

provides a mechanism through wrapped objects, allowing developers to invoke multiple utility s sequentially in a fluent style without assigning intermediate results to variables. The primary entry point is the _.chain(obj) , 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. Alternatively, invoking as a with _(obj) also produces a wrapped object that supports , and the chain concludes by calling .value() to unwrap and retrieve the final result. This wrapping leverages the object (_) as a foundational accessor that holds altered versions of its utility s, enabling object-oriented style invocations. For example, to double numbers in an and 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)();  
This yields [4, 6], demonstrating how composes operations like and seamlessly. 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)();  
This produces a word count object, illustrating 's utility for multi-step data transformations. 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. 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. 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.

Iteratees and Shorthands

Standard Iteratee Usage

In Underscore.js, an iteratee serves as a callback passed to various collection-processing methods, such as _.map or _.filter, to transform, , or otherwise operate on each of the input collection. These methods invoke the iteratee for every in the collection, providing it with three arguments: the current (), its (for arrays) or (for objects), and a reference to the entire collection. This standardized signature enables consistent behavior across functions, allowing developers to access contextual information during iteration without additional setup. 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. 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. These notations simplify code for property access and conditional matching, promoting readability in functional-style programming. 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. 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.

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. 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 objects or DOM elements. To extend or alter this default behavior, developers can override _.iteratee entirely by reassigning it to a function, allowing the addition of new styles tailored to specific needs. For instance, a could recognize regular expressions as input values, converting them into predicate functions for pattern-based filtering, thus supporting like /^prefix/ in place of verbose regex tests. This overriding approach provides a clean entry point for personalization without modifying core library code. 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 , such as interpreting 'a.0.b' to access the b of the first element in a, thereby extending Underscore.js's matching capabilities for mixed -object . This integration facilitates hybrid usage scenarios, where Lodash's more permissive path syntax enhances Underscore's lighter footprint. These customization options are particularly valuable for adapting Underscore.js to domain-specific requirements or optimizing in constrained environments. For example, in applications with complex data models—such as those involving framework-specific props or responses—custom shorthands can reduce boilerplate and improve readability, while tuned iteratee logic might resolutions for repeated operations on large collections. Overall, such overrides empower developers to evolve the library's utility layer without forking the source.

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 principles, allowing developers to process data declaratively. The core 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. 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'
});
This supports sparse arrays through duck-typing, ensuring consistent iteration even with undefined elements. 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, /key, and . 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 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
Iterate shorthands, such as _.property('key'), can simplify these operations by providing property-extracting functions. 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]]
Sparse arrays are handled consistently, though the focus remains on the invoked method's behavior. Finally, _.pluck offers a concise way to extract a single property from each object in a list, equivalent to with an iteratee that accesses that property. Its signature is _.pluck(list, propertyName), returning an 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']
This function maintains the input's structure for sparse cases, providing a streamlined to explicit _.map calls.

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 paradigms, allowing developers to accumulate results through iteratees or compute extrema and groupings without mutating the original collection. 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. If the collection is empty and no memo is provided, the function returns undefined. 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. 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. 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. Finally, _.size(obj) simply returns the count of values in an , object, or string-like collection, equivalent to length for arrays but handling objects via . Examples include _.size([1, 2, 3]) returning 3 and _.size({one: 1, two: 2}) returning 2; it yields 0 for empty collections.

Array Functions

Selection and Extraction

The selection and extraction functions in Underscore.js provide utilities for retrieving subsets of arrays based on or for cleaning and reshaping array contents by removing unwanted elements or 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. The _.first function, also aliased as _.head, retrieves the first element of an 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 . For example, _.first([1, 2, 3, 4], 2) returns [1, 2]. This is useful for extracting prefixes in data processing workflows. Similarly, _.last returns the last element or the last n elements of an , with behavior mirroring _.first for oversized n. It supports the same aliasing and edge-case handling as its counterpart. 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. Extraction also includes reshaping operations like _.flatten, which recursively flattens nested s into a single-level unless the optional depth parameter is provided: true or 1 for only the outermost level, a positive 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 parsing or responses. 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 pipelines.

Set Operations

Underscore.js provides several functions that perform set-like operations, enabling developers to compute unions, intersections, differences, and other relational manipulations on of primitive values or objects. These functions treat 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. The 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. 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. 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. 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. 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. 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.

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 , filtering, and subsetting operations. These functions enable developers to retrieve details, for or presence of keys, and create modified copies without altering the originals, promoting patterns in . All operations are designed to work with plain objects, handling enumerable own while respecting JavaScript's prototypal . 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. For property presence checks, _.has(object, key) determines whether the object has an own property with the specified key, returning a . This is particularly useful for avoiding errors when accessing potentially 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 , returning true for an empty object {} and false otherwise. These utilities support robust object validation in codebases handling dynamic data structures. 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 . For a standalone shallow , _.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. 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 , such as extracting user profiles or removing sensitive fields, while preserving the original object intact.

Type Assertions

Underscore.js provides a suite of utility functions for asserting and validating the types of objects, offering more reliable alternatives to native methods like typeof or instanceof, which often fall short in handling edge cases such as arrays or values. These functions, prefixed with is, return booleans and are designed for in functional-style code, enabling developers to check object categories before operations like or . They are particularly useful in environments where type or prototype pollution might occur, ensuring predictable behavior across browsers. The core type-checking functions include _.isArray(object), which determines if the input is an , returning true for [] unlike the native typeof that yields 'object'. Similarly, _.isObject(value) verifies if the value is a non-primitive object, encompassing plain objects {}, s, and functions, but excluding null. For primitives, _.isNumber(object) confirms numeric types including , _.isString(object) checks strings, _.isFunction(object) identifies functions, _.isDate(object) validates instances, _.isRegExp(object) detects regular expressions, and _.isNaN(object) specifically tests for 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. 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
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. Beyond basic type checks, _.isEqual(a, b) performs a deep 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 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
The function short-circuits on mismatched types or lengths, providing efficient validation for complex data. 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)
This utility integrates seamlessly with type assertions for robust property validation in Underscore.js workflows.

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. The _.bind function creates a new that, when invoked, has its this keyword set to a specified object, while also supporting of initial arguments. Its signature is _.bind([function](/page/Function), object, *arguments), where the first is the to , the second is the object, and subsequent optional arguments are pre-applied. This is particularly useful in event handlers or callbacks where maintaining object is . For example:
javascript
var func = [function](/page/Function)(greeting) { return greeting + ': ' + this.name; };
func = _.bind(func, {name: 'moe'}, 'hi');
func();
=> 'hi: moe'
In this case, the bound ensures this.name resolves to 'moe' regardless of the calling . Complementing binding, _.partial facilitates by returning a new with some arguments already supplied, without altering the this . The is _.partial([function](/page/Function), *arguments), where placeholders denoted by the _ symbol allow later arguments to be inserted at . 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
Here, sub5 pre-fills the first argument as 5, while subFrom20 uses _ to defer the first argument, enabling flexible invocation. 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 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);
This ensures that this.label remains accessible within the event handlers, avoiding common pitfalls in JavaScript callbacks.

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. 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. The function accepts a wait duration in milliseconds and an optional immediate boolean to execute on the leading edge instead of the trailing edge. Complementing debounce, the _.throttle function limits a function to execute at most once every specified interval, ensuring periodic invocation even amid continuous calls. 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. 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. 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();. For composition, Underscore.js offers tools to chain or modify functions, facilitating modular code design akin to patterns. The 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));. Here, renderResults only proceeds after all items in collection complete their processing. 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. An example is var compute = _.compose(square, addOne); compute(2);, which yields 9 by adding 1 to 2 and then squaring the result. 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. 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. This approach supports conditional execution or argument adjustment without altering the core function.

References

  1. [1]
    Underscore.js
    Underscore is a JavaScript library that provides a whole mess of useful functional programming helpers without extending any built-in objects.Modules/underscore.js · Underscore-esm.js · Debounce.js · Template.js
  2. [2]
    jashkenas/underscore: JavaScript's utility _ belt - GitHub
    Underscore.js is a utility-belt library for JavaScript that provides support for the usual functional suspects (each, map, reduce, filter...)
  3. [3]
    underscore - NPM
    Jul 24, 2024 · Underscore.js is a utility-belt library for JavaScript that provides support for the usual functional suspects (each, map, reduce, filter...)
  4. [4]
  5. [5]
    Underscore.js: Functional Programming for jQuery | Hacker News
    Oct 28, 2009 · Quick Summary: Underscore provides all the functional programming goodies that you would expect with Prototype.js or Ruby, but without extending ...Missing: extensions | Show results with:extensions
  6. [6]
  7. [7]
    Release 1.8.3 · jashkenas/underscore
    - **Version Number**: 1.8.3
  8. [8]
    Release 1.13.0 · jashkenas/underscore
    - **Release Date:** 09 Apr
  9. [9]
    Release 1.13.7 · jashkenas/underscore
    - **Release Date:** 24 Jul
  10. [10]
    underscore - Snyk Vulnerability Database
    An important project maintenance signal to consider for underscore is that it hasn't seen any new versions released to npm in the past 12 months, and could be ...
  11. [11]
    underscore-esm.js
    Returns the results of applying the iteratee to each element of obj . In contrast to _.map it returns an object. function mapObject(obj, iteratee, context) ...
  12. [12]
    JavaScript evolution: From Lodash and Underscore to vanilla
    Dec 12, 2024 · Lodash was created by John-David Dalton in 2012 as a fork of Underscore. It was created to provide a more consistent API and better performance.
  13. [13]
    Lodash Basics - A JavaScript Library - Web Reference
    The original author of Lodash is John-David Dalton. Lodash was forked from Underscore.js and in 2013, joined the Dojo Foundation and is now a part of the OpenJS ...
  14. [14]
    Differences between Lodash and Underscore.js - Stack Overflow
    Dec 9, 2012 · Because Lodash is updated more frequently than Underscore.js, a lodash underscore build is provided to ensure compatibility with the latest ...underscore backwards compatibility - backbone.js - Stack OverflowWhy did Underscore.js remove support for AMD? - Stack OverflowMore results from stackoverflow.comMissing: ES3 ESM
  15. [15]
  16. [16]
    Partially Applying Functions in JavaScript Using Underscore.js
    May 6, 2014 · Starting with version 1.6.0 which was released in February 2014, the _.partial function is now a lot more flexible and allows for some
  17. [17]
    Migrating from Underscore to Lodash - Dropbox Tech Blog
    Sep 5, 2018 · Lodash is a utility library composed of many individual modules. It can be used as a complete library, as individual modules, or as a custom ...
  18. [18]
  19. [19]
  20. [20]
    underscore.js
    Underscore is called as a function, it returns a wrapped object that can be used OO-style. This wrapper holds altered versions of all functions added through _ ...
  21. [21]
    Why Underscore.js chain() method is not lazy? - Stack Overflow
    Jul 17, 2013 · There's two main problems: (1) it's a lot more work and (2) it's a completely opposite architecture requiring all of underscore to be written ...What is the preferred way of chaining Underscore.js functions?Filtering array with underscore.js - javascript - Stack OverflowMore results from stackoverflow.comMissing: deprecated? | Show results with:deprecated?
  22. [22]
    5 JavaScript Libraries You Should Say Goodbye to in 2025
    Dec 7, 2024 · We highlight five JavaScript libraries that are likely to become obsolete in 2025 and why it's time to move on. Also: we list alternatives!
  23. [23]
    Underscore.js
    Summary of each segment:
  24. [24]
  25. [25]
    iteratee.js
    - **What is an iteratee?**: An iteratee is a callback function generated by `_.iteratee` for use in Underscore.js methods, customizable via predicate/iteratee shorthand styles.
  26. [26]
    Underscore.js
    Summary of each segment:
  27. [27]
    Underscore.js
    Summary of each segment:
  28. [28]
  29. [29]
    iteratee.js
    External wrapper for our callback generator. Users may customize _.iteratee if they want additional predicate/iteratee shorthand styles. This abstraction hides ...
  30. [30]
  31. [31]
  32. [32]
  33. [33]
    Underscore.js
    Summary of each segment:
  34. [34]
    Underscore.js
    Summary of each segment:
  35. [35]
    Underscore.js
    Summary of each segment:
  36. [36]
    Underscore.js
    Summary of each segment:
  37. [37]
    Underscore.js
    Summary of each segment:
  38. [38]
  39. [39]
    Underscore.js
    Summary of each segment:
  40. [40]
    Underscore.js
    Summary of each segment:
  41. [41]
  42. [42]
  43. [43]
  44. [44]
  45. [45]
  46. [46]
  47. [47]
  48. [48]
  49. [49]
  50. [50]
  51. [51]
  52. [52]
  53. [53]
  54. [54]
  55. [55]
  56. [56]
  57. [57]
  58. [58]
  59. [59]
  60. [60]
  61. [61]
  62. [62]
  63. [63]
  64. [64]
  65. [65]
  66. [66]
  67. [67]
  68. [68]
  69. [69]
  70. [70]
  71. [71]
  72. [72]
  73. [73]
  74. [74]
  75. [75]
  76. [76]
  77. [77]
  78. [78]
  79. [79]
  80. [80]
  81. [81]
  82. [82]