Ways to determine if something is a plain object in JavaScript

Viewed 982

I've recently stumbled on this function which determines if something is a plain object is JavaScript:

function isPlainObject (value){
  if (typeof value !== 'object' || value === null) return false;

  let proto = value;
  while (Object.getPrototypeOf(proto) !== null) {
    proto = Object.getPrototypeOf(proto);
  }

  return Object.getPrototypeOf(value) === proto;
};

Source: https://github.com/redux-utilities/redux-actions/blob/master/src/utils/isPlainObject.js

I would like to know:

  1. If the following approach will do exactly the same stuff?
  2. If so, can it be considered more effective?
function isPlainObj(value){
  if (typeof value !== 'object' || value === null) return false;
  let obj = {};
  return Object.getPrototypeOf(value) === Object.getPrototypeOf(obj)
}
3 Answers

Checking if a value is a plain object:

/*
 isPlainObject({}) // true
 isPlainObject() // false
 isPlainObject(null) // false
 isPlainObject(true) // false
 isPlainObject('1') // false
 isPlainObject([]) // false
 isPlainObject(new Function()) // false
 isPlainObject(new Date()) // false
 */
const isPlainObject = value => value?.constructor === Object;

Excludes nulls, scalars, arrays, functions and any extended type other than Object.

  1. No. The former walks through the whole prototype chain but returns true only if such chain is composed by 1 prototype (so, your first example is kinda pointless)
  2. Yes and No. Yes, it's more effective, no need to loop everything just to check the prototype is the Object.prototype one. No, it performs a needless operation.

This is how I'd do it:

const isPlainObj = value => !!value &&
                            Object.getPrototypeOf(value) === Object.prototype;

No need to go too fancy, if all you want to know is that value prototype is Object.prototype

P.S. There is one thing your initial example does that other examples, including mine, don't: it works with foreign objects, which are objects coming from different realms (i.e. iframes). I don't think this use case exists in 2021, but if your app/site passes objects between different windows/frames then the first function would return true for those objects too, while my suggestion, or yours, won't. Yet, there's no need to loop through the whole chain, you can simply do this instead:

function isPlainObj(value) {
          // it's truthy
  return  !!value &&
          // it has a prototype that's also truthy
          !!(value = Object.getPrototypeOf(value)) &&
          // which has `null` as parent prototype
          !Object.getPrototypeOf(value);
}

This grabs the proto once or maximum twice ensuring its chain ends up with null, which is usually the common literal case.

Yet, I think foreign objects are non existent these days, so I'd stick with my suggested version.

ToolJS has a method under it's "Obj" module that checks if an object is infact a plain object literal.

Ref: ToolJS Object Module

Get the code on NPM or through its CDN and use as shown below

// export the methods in the "Obj" module
var $ = ToolJS.export("Obj");

var myObj = {name: "John Doe"};
var myArr = [1,2,3]; // note that arrays are of type object but are obviously not plain objects
var myEl = document.getElementById("elem"); // elements are also objects

$.isObj(myObj); // => true
$.isObj(myArr); // => false
$.isObj(myEl); // => false

You can check out it's full documentation here

Under the hood, the method checks the item type is not null or undefined but an object, then checks its constructor to see if its even an object, after which it makes sure that its not an array and finally converts it to string to see if its a plain object.

Related