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:
this.__values__: The reference pointer to the underlying source array containing the raw sequential elements.this.__index__: The primary iteration pointer. It maintains the current sequential index of the traversal, initialized at0for forward passes.this.__dir__: The direction indicator. A value of1signifies forward iteration, while-1directs the pointer to walk backward through the array.this.__actions__: An internal stack of transformation descriptors (such as filters, mappers, or slice boundaries) applied to the value at each iteration step.this.__views__: An array tracking slice parameters, defining custom viewing windows (start, end) across the target array without slicing the array in memory.
How LazyWrapper.prototype.next Operates
When LazyWrapper.prototype.next() is invoked, Lodash
directly steps through the sequential data using the following
cycle:
- Pointer Validation: The engine checks whether
this.__values__has been exhausted by comparing the pointerthis.__index__against the array bounds (derived from__values__.lengthor configured slice ranges). - Value Fetching: It reads the element at
this.__values__[this.__index__]. - Pointer Increment: The pointer
this.__index__is incremented bythis.__dir__. - 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, advancingthis.__index__until a value passes all predicates or the end of the collection is reached. - Standard Yield: Once a value successfully passes
through all transformational actions, the function returns a standard
iterator result object:
{ done: false, value: processedValue }. Ifthis.__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.