Skip to main content

Clean object without a prototype

A clean object (also called an object dictionary) is an object with no prototype, that is, without Object.prototype and all of its built-in methods such as toString or hasOwnProperty. Such an object is a perfect fit for pure data stores, dictionaries and maps, where you want to be sure that no built-in property collides with your keys.

Theory

TL;DR

  • A clean object is created with Object.create(null).
  • For it Object.getPrototypeOf(obj) is null, there is no prototype chain at all.
  • It has no toString, no hasOwnProperty and no special __proto__ behaviour.
  • So keys like toString or __proto__ become ordinary keys and break nothing.
  • Check for a property with Object.prototype.hasOwnProperty.call(obj, key) or Object.hasOwn(obj, key).
  • The modern alternative for dictionaries is Map.

Quick example

The simplest and correct way:

javascript
const obj = Object.create(null);

This creates an object without a prototype: obj.__proto__ is undefined and Object.getPrototypeOf(obj) is null.

Let us check:

javascript
const obj = Object.create(null); console.log(Object.getPrototypeOf(obj)); // null console.log('__proto__' in obj); // false console.log(typeof obj.toString); // "undefined"

Such an object has no inheritance chain: it does not even inherit from Object.prototype.

Why this is useful

Ordinary objects inherit a pile of built-in methods:

javascript
const normal = {}; console.log(Object.getPrototypeOf(normal) === Object.prototype); // true // These methods are available console.log(normal.hasOwnProperty); // [Function] console.log(normal.toString); // [Function]

That can be dangerous when your keys overlap with those names:

javascript
const map = {}; map.toString = 'value'; // may break code that later calls map.toString()

A clean object removes the problem:

javascript
const map = Object.create(null); map.toString = 'value'; // just a key console.log(map.toString); // "value"

Example use: dictionaries and maps

javascript
const dict = Object.create(null); dict.apple = 1; dict.banana = 2; dict['__proto__'] = 3; // safe, it does not affect the prototype console.log(dict.apple); // 1 console.log(dict['__proto__']); // 3

On ordinary objects __proto__ has special behaviour (it is an accessor on Object.prototype), while here it is a plain property. That is why clean objects are often used for caches and for parsing data that came from outside: they are naturally protected against prototype pollution.

The drawback: no built-in methods

javascript
const obj = Object.create(null); obj.name = 'Alice'; console.log(obj.hasOwnProperty('name')); // TypeError: obj.hasOwnProperty is not a function

If you need to check whether a property exists, use the safe form:

javascript
console.log(Object.prototype.hasOwnProperty.call(obj, 'name')); // true console.log(Object.hasOwn(obj, 'name')); // true, the modern form

The same applies to toString(): such an object cannot simply be concatenated with a string, and in some environments console.log prints it as [Object: null prototype].

The alternative: use Map

The modern way to store dictionaries:

javascript
const map = new Map(); map.set('apple', 1); map.set('__proto__', 2); console.log(map.get('__proto__')); // 2

Map solves the same problem, but it accepts keys of any type, objects included, preserves insertion order and exposes a size property.

Summary

ApproachPrototypeAccess to built-in methodsSuitable for dictionaries
{}Object.prototypeyes (toString, hasOwnProperty)risky
Object.create(null)nullnoyes
new Map()not a plain objectthe set, get, has methodsyes, the modern option

To create a clean object without Object.prototype, use const obj = Object.create(null): it inherits nothing and is a perfect fit for safe dictionaries.

Common mistakes

  • Calling obj.hasOwnProperty(key) on a clean object. You get a TypeError, because the method simply is not there. Use Object.hasOwn(obj, key).
  • Confusing Object.create(null) with {}. The latter inherits from Object.prototype with everything that implies.
  • Passing a clean object into code that expects a normal one. A library that internally calls value.toString() will throw.
  • Assuming JSON.stringify does not work. It works fine, the trouble is with implicit string coercion.
  • Using a clean object where Map fits better. If the keys are not strings, or you need size and ordering, reach for Map.
  • Forgetting that an object from JSON.parse is an ordinary one. Parsing external JSON yields an object with a prototype, so validate the __proto__ key before merging.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.