How Lodash _.forOwn Iterates Over Own Properties

This article explores how the Lodash utility library implements _.forOwn to restrict iteration strictly to an object's directly attached enumerable keys. By examining the underlying architectural layers—ranging from internal keys extraction and defensive ownership checks to native reflection mechanisms—this overview demonstrates how _.forOwn reliably isolates an object's own properties while shielding execution from prototype chain inheritance and property shadowing vulnerabilities.

The Underlying Iteration Pipeline

At a high level, _.forOwn serves as a specialized wrapper around Lodash's generic iteration engine, baseFor. While standard JavaScript for...in loops traverse both an object’s own properties and its entire prototype chain, _.forOwn prevents prototype traversal by pre-filtering property keys or combining iteration with strict ownership checks.

In modern versions of Lodash, _.forOwn delegates directly to an internal helper function, typically named baseForOwn. This internal function coordinates iteration by pairing a directional loop handler with an enumerable keys retriever:

function baseForOwn(object, iteratee) {
  return object && baseFor(object, iteratee, keys);
}

By supplying its own internal keys retrieval function rather than keysIn (which powers _.forIn), Lodash forces the loop algorithm to operate exclusively on direct keys.

Delegation to Native Object.keys

The primary layer of security and performance in _.forOwn is its dependency on Object.keys (or its internal equivalent, baseKeys).

In ECMAScript environments, Object.keys() is defined by the runtime specification to return an array of an object's own enumerable string-keyed properties. By collecting these keys prior to iteration, Lodash delegates the boundary enforcement directly to the JavaScript engine's C++ layer. Inherited properties from Object.prototype or custom constructor prototypes are eliminated from the iteration target set before the user-supplied iteratee function is ever invoked.

Defensive Ownership Checks

When handling legacy runtimes or complex objects where prototype traps might exist, Lodash guarantees encapsulation by shielding against shadowed or severed methods.

A common failure point in native JavaScript occurs when attempting to call object.hasOwnProperty(key) directly:

  1. The target object may have been initialized via Object.create(null), leaving it without access to Object.prototype.
  2. The property name hasOwnProperty may be shadowed or overwritten on the object itself (e.g., { hasOwnProperty: false }).

Lodash circumvents this by referencing the uncorrupted prototype method internally:

const hasOwnProperty = Object.prototype.hasOwnProperty;

function hasPath(object, key) {
  return object != null && hasOwnProperty.call(object, key);
}

By leveraging Function.prototype.call with the root Object.prototype.hasOwnProperty, Lodash safely verifies whether a property exists directly on the target instance, completely bypassing the instance's own prototype chain.

Strict Enumerable Filtering

Not all own properties should be traversed during functional iteration. JavaScript allows properties to be marked as non-enumerable via Object.defineProperty.

_.forOwn strictly adheres to enumerability specifications. In environments requiring fallback mechanisms for complex types (such as arguments objects, typed arrays, or prototype objects with non-standard descriptors), Lodash uses helper utilities like isPrototype and custom index validators to ignore non-enumerable internal attributes (like length on functions or constructor references on prototypes).

Mitigation of Prototype Pollution

By encapsulating property discovery within safe retrieval boundaries, _.forOwn acts as a guard against prototype pollution attacks.

When external payloads inject malicious properties into an application's shared prototypes (such as Object.prototype), standard for...in loops will process those unexpected keys. Because _.forOwn anchors its key list strictly to the immediate object using Object.keys and safe hasOwnProperty checks, injected prototype properties are systematically ignored. This guarantees that iteration logic processes only data explicitly set on the target instance itself.