← Technology Archive

Historical archive

Understanding the JavaScript Prototype Chain

A practical explanation of JavaScript value types, typeof, null versus undefined, constructor prototypes, property lookup, and Object.getPrototypeOf().

JavaScript value types

Every JavaScript value belongs to a data type. The original language model commonly described six:

  • number: integers and floating-point values
  • string: text made from characters
  • boolean: true or false
  • undefined: a missing or uninitialized value
  • null: an intentional absence of value
  • object: a collection of values

Numbers, strings, and booleans are primitive values. Objects are composite values. Objects include ordinary objects, arrays, and functions, although functions have special callable behavior.

Modern JavaScript also includes symbol and bigint primitive types.

The typeof operator

typeof returns a string describing a value:

  • Numbers, strings, and booleans return "number", "string", and "boolean".
  • Functions return "function".
  • undefined returns "undefined", which can be useful when safely checking a possibly undeclared variable.
  • Objects, arrays, and null return "object".

Arrays are specialized objects. The fact that typeof null returns "object" is a historical language bug; null is conceptually an absence value rather than an ordinary object.

null and undefined

They both represent missing values but behave differently during numeric conversion:

console.log(5 + null);      // 5
console.log(5 + undefined); // NaN

null converts to zero in this numeric context, while undefined converts to NaN.

The object prototype chain

Every ordinary JavaScript object has an internal link to another object called its prototype. That prototype can have its own prototype. The chain ends at null, which has no prototype.

Every constructable function has a prototype property. When called with new, the newly created instance uses that object as its prototype and can access its shared properties and methods.

function F1() {}
function F2() {}

F1.prototype.print = function () {
  console.log('F1');
};

var a = new F1();
a.print(); // F1

var b = new F2();
console.log(b.print); // undefined

The instance inherits through its internal [[Prototype]] link, exposed historically as __proto__. Constructors store shared methods on their prototype objects.

When code reads a property, the engine first checks the object itself. If it is not there, the engine checks the object’s prototype, then that prototype’s prototype, continuing until it reaches Object.prototype and finally null. If no matching property exists, the result is undefined.

An own property shadows a prototype property with the same name.

Inspect a prototype with Object.getPrototypeOf()

Object.getPrototypeOf() is the standard way to retrieve an object’s prototype.

function Person() {
  this.name = '';
  this.age = 0;
}

var person = new Person();
Object.getPrototypeOf(person);

The instance can access constructor:

person.constructor.name; // "Person"

But constructor is not an own property of the instance. It is inherited from Person.prototype:

person.hasOwnProperty('constructor'); // false
Person.prototype.hasOwnProperty('constructor'); // true

The constructor property links a prototype object back to its constructor. When replacing a constructor’s prototype object, it is often useful to restore the appropriate constructor property.

In summary, Person owns Person.prototype; an instance does not own a prototype property, but its internal prototype points to Person.prototype, allowing it to inherit constructor and other shared members.