Javascript prototype chain

Javascript prototype chain

Javascript objects have an internal property called [[Prototype]] for property lookup and inheritance.

When looking for an object property, javascript checks the object's own property first, if not found, it will continue with the object's [[Prototype]], if not found again, look for [[Prototype]] of its [[Prototype]] until reaching null or the property found.


To access [[Prototype]] of an object:

  • Object.getPrototypeOf(obj), Reflect.getPrototypeOf(obj) to get.
  • Object.setPrototypeOf(obj, prototype), Reflect.setPrototypeOf(obj, prototype) to set.
  • [non standard] Object.prototype.__proto__ getter and setter.
  • Object initializer syntax with prototype specification: {__proto__: prototype}.
  • Object initializer without prototype specification: {foo: 'bar'} set object's [[Prototype]] to Object.prototype.
  • Object.create(prototype).
  • new Foo or new Foo() create object whose [[Prototype]] is Foo.prototype.
  • Array initializer syntax: [] creates object whose [[Prototype]] is Array.prototype.
  • Function definition syntax: () => {}, function() {} create object whose [[Prototype]] is Function.prototype.
  • RegExp literals /abc/ creates an object whose [[Prototype]] is RegExp.prototype, equivalent of new RegExp('abc').

Note:

Object initializer with prototype specification is a syntax, it is not the same as Object.prototype.__proto__ setter. Moreover, an object can have its own __proto__ property key.

Chrome browser displays [[Prototype]] internal property in grey color
  • The object's [[Prototype]] can be set via obj.__proto__ = prototype because that object inherits Object (its [[Prototype]] chain has Object.prototype), and assigning obj.__proto__ actually calls Object.prototype.__proto__ setter.

Some quirks:

  • {'__proto__': prototype} is the same as {__proto__: prototype} and {"__proto__": prototype}.
  • {__proto__} (shorthand property), {['__proto__']: prototype} , {__proto__(){}} {get __proto__(){}}are not prototype specifier syntax, they are typical key-value assignments.
  • {a: 1, a: 2} gives {a: 2} (no error), while {__proto__: v1, __proto__: v2}, {__proto__: v1, '__proto__': v2}, {'__proto__': v1, '__proto__': v2} all thow syntax error: Uncaught SyntaxError: Duplicate __proto__ fields are not allowed in object literals. These are syntax error mean the parser will throw errors even before v1, v2 are evaluated.
  • If the value provided to the prototype specifier is not an object (whose prototype chain contains Object.prototype) or null, the specifier is a no-op.
  • JSON.parse('{"__proto__": null}') returns an object with its own property __proto__ key.

Other notes:

  • Object.getPrototypeOf(Object.prototype) is null.
  • You cannot assign a cyclic prototype chain.
Object.setPrototypeOf(a, a)
06:31:41.419 VM410:1 Uncaught TypeError: Cyclic __proto__ value
    at Function.setPrototypeOf (<anonymous>)
    at <anonymous>:1:8
  • Object.getPrototypeOf(1) === Number.prototype returns true because 1 is temporarily wrapped by new Number(1) (not Number(1) which returns the primitive 1 value).
  • typeof null returns "object". From MDN web docs:

From MDN web docs:

  • functions declared with function keyword declaration have prototype property. Functions defined with arrow syntax do not have this property.
Buy Me A Coffee