How Lodash Executes Prototype Next on Arrays

Lodash processes large sequential arrays efficiently by deferring execution through an internal lazy evaluation engine rather than evaluating transformations eagerly. At the heart of this system is the internal LazyWrapper construct, which manages sequential iteration pointers natively through LazyWrapper.prototype.next. This article details the internal pointer architecture within Lodash, how properties such as __index__ and __values__ control iteration flow, and how calling .next() evaluates array data sequentially step by step.

The Core Architecture: LazyWrapper

Standard Lodash array operations (like _.map or _.filter) run eagerly through utility functions such as arrayEach or baseEach, which loop through entire collections using classic index-incrementing while loops. However, when wrapped via chaining expressions—typically initiated with _(array)—Lodash swaps this eager pattern for an instance of LazyWrapper.

LazyWrapper conforms directly to the ECMAScript Iterator protocol by defining a native next method on its prototype (LazyWrapper.prototype.next). This method allows array elements to be processed one at a time, halting execution until the next value is requested.

Internal Iteration Pointers

To cleanly track traversal through a sequential array without creating intermediate array allocations, LazyWrapper maintains several internal properties that serve as traversal pointers and state containers:

How LazyWrapper.prototype.next Operates

When LazyWrapper.prototype.next() is invoked, Lodash directly steps through the sequential data using the following cycle:

  1. Pointer Validation: The engine checks whether this.__values__ has been exhausted by comparing the pointer this.__index__ against the array bounds (derived from __values__.length or configured slice ranges).
  2. Value Fetching: It reads the element at this.__values__[this.__index__].
  3. Pointer Increment: The pointer this.__index__ is incremented by this.__dir__.
  4. Action Pipeline Traversal: The acquired element is passed through the queued sequence stored in this.__actions__. If an action represents a filter that rejects the element, the loop continues internally, advancing this.__index__ until a value passes all predicates or the end of the collection is reached.
  5. Standard Yield: Once a value successfully passes through all transformational actions, the function returns a standard iterator result object: { done: false, value: processedValue }. If this.__index__ crosses the boundary limit, it returns { done: true, value: undefined }.

By isolating iteration state inside this.__index__ and exposing it via prototype.next, Lodash avoids the overhead of generating temporary arrays between operations while providing native, pull-based stream processing over plain JavaScript arrays.