How Lodash isPlainObject Validates Null Prototypes

Lodash’s _.isPlainObject method determines whether a given value is a plain object—specifically, an object created by the Object constructor or one with a [[Prototype]] of null. This article breaks down the internal mechanics of how Lodash checks and validates objects explicitly created with Object.create(null), detailing the tag verification, prototype extraction, and early return logic that permits null prototypes while rejecting other complex object types.

The Definition of a Plain Object in Lodash

In JavaScript, a plain object is generally considered to be an object created by literal syntax ({}) or via new Object(). However, developers frequently use Object.create(null) to produce clean dictionary structures that do not inherit built-in properties from Object.prototype, such as toString, valueOf, or hasOwnProperty.

Lodash treats both categories as plain objects. To accommodate null prototype objects without erroneously accepting class instances, DOM nodes, or built-in objects, Lodash applies a sequence of specific internal validations.

Step 1: Base Type and Tag Verification

Before inspecting prototype chains, _.isPlainObject ensures the input value is fundamentally shaped like an object.

  1. isObjectLike Check: Lodash verifies that the value is non-null and that typeof value === 'object'. Because Object.create(null) produces a non-null type of 'object', it passes this gate.
  2. Internal Tag Check: Lodash resolves the internal [[Class]] tag of the value using an internal getTag utility (or a fallback to Object.prototype.toString.call). For an object created with Object.create(null), the tag resolves to [object Object].

If either condition fails, the function immediately returns false.

Step 2: Prototype Extraction

Once the object passes the preliminary type tests, Lodash retrieves its direct prototype:

const proto = Object.getPrototypeOf(value);

For standard object literals ({}), proto resolves to Object.prototype. For objects instantiated with Object.create(null), Object.getPrototypeOf(value) returns null.

Step 3: The Explicit Null Prototype Check

Lodash includes an explicit guard statement specifically designed for null prototypes:

if (proto === null) {
  return true;
}

Because proto is strictly equal to null, this condition evaluates to true instantly. Lodash terminates execution here and returns true, skipping all subsequent constructor-matching logic.

Why the Short-Circuit Is Necessary

Standard plain objects undergo further verification to verify their constructor:

const Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor;
return typeof Ctor == 'function' && Ctor instanceof Ctor &&
  funcToString.call(Ctor) == objectCtorString;

If Lodash did not explicitly handle the proto === null case early, attempting to access properties or call hasOwnProperty on a null prototype would cause a TypeError (since proto would be null). By placing if (proto === null) return true; before checking proto.constructor, Lodash both avoids runtime errors and formally accepts bare dictionary objects as valid plain objects.