Lodash _.isPlainObject with Object.create(null)
This article examines how the Lodash JavaScript library’s
_.isPlainObject function evaluates objects instantiated
with Object.create(null). It covers the exact boolean
result returned by the utility, the internal implementation logic that
determines this outcome, and why this design choice matters when
handling prototype-less dictionary objects in modern JavaScript.
The Short Answer
In modern versions of Lodash (v4.0.0 and later), calling
_.isPlainObject(Object.create(null)) returns
true.
Lodash treats an object with a null prototype as a plain
object, alongside standard object literals ({}) and objects
created directly via new Object().
const lodash = require('lodash');
const nullProtoObj = Object.create(null);
console.log(lodash.isPlainObject(nullProtoObj)); // true
const literalObj = {};
console.log(lodash.isPlainObject(literalObj)); // trueHow Lodash Evaluates
Object.create(null)
To understand why Object.create(null) evaluates to
true, consider Lodash's definition and implementation of
_.isPlainObject. Lodash defines a plain object as:
- An object created by the
Objectconstructor. - An object with a
[[Prototype]]ofnull.
Internally, the _.isPlainObject algorithm follows these
steps:
- Type and Tag Check: It verifies whether the input
is "object-like" (non-null and
typeof value === 'object') and that its internal[[Class]]tag matches[object Object]. - Null Prototype Short-Circuit: It retrieves the
prototype using
Object.getPrototypeOf(value). If the prototype is explicitlynull, the function immediately returnstrue. - Prototype Chain Resolution: For objects with
prototypes, it traverses up the prototype chain until it reaches the
top-level prototype (
Object.prototype) and ensures the object's direct prototype matches this terminal prototype.
Because Object.create(null) sets the internal
[[Prototype]] directly to null, it fulfills
the check in step two without needing to traverse the prototype
chain.
Why This Behavior Matters
In JavaScript, Object.create(null) is commonly used to
create clean map or dictionary objects. Because these objects do not
inherit from Object.prototype, they are free from built-in
properties like toString, valueOf, or
hasOwnProperty. This prevents prototype pollution and key
collision vulnerabilities.
Earlier versions of utility libraries often relied strictly on checking constructor properties:
// Native naive check
function isPlain(obj) {
return obj != null && obj.constructor === Object;
}
isPlain(Object.create(null)); // false, because obj.constructor is undefinedBecause Object.create(null) lacks a
constructor property, naive checks incorrectly identify it
as not being a plain object. Lodash avoids this pitfall by checking
prototypes directly, ensuring that clean, prototype-free key-value maps
are correctly classified as plain objects.