Understanding prototypes, constructors, and prototype objects in JavaScript (walk-through using examples)

Nirab Pant
7 min readOct 26, 2021

--

This is a companion article to the first 40 minutes of the following video lecture on various OOP concepts in JavaScript: https://www.youtube.com/watch?v=-N9tBvlO9Bo.

Both the video lecture and the contents of this article draw upon concepts covered in JS 120 and JS 225 as part of Launch School’s curriculum on the fundamentals of software engineering.

Example 1

Fig 1: Schematic showing how an object literal, ‘obj’, inherits methods and properties from ‘Object.prototype’, also known as the ‘null prototype object’.

We’ll start by defining a simple object literal obj on lines 2–5. We can confirm that it is indeed of type Object using typeof, and validate that it has the properties that we defined on it, namely, color and amount, using the hasOwnProperty method.

Now, the first question that arises is how did obj get access to the hasOwnProperty method in the first place? When we manually created obj on lines 2–5, we defined just the two properties on it; nonetheless, we were able to invoke hasOwnProperty as if it were defined on obj.

In fact, we can invoke this very method to verify that it does not come from obj, i.e., obj.hasOwnProperty('hasOwnProperty') logs false (see line 14 in the code snippet).

The answer to this lies in the schematic shown in Fig. 1 which serves as our first model of how inheritance works in JavaScript.

As illustrated by the schematic, because we’ve manually created obj using the object literal syntax, behind the scenes, JavaScript uses the Object constructor to create the new object. What’s more, it sets the [[Prototype]] property of obj to reference the constructor’s prototype object, Object.prototype. It is from this object that obj inherits the hasOwnProperty method. In the code snippet above (line 24), this can be verified by logging Object.prototype.hasOwnProperty('hasOwnProperty') which evaluates to true.

The [[Prototype]] property is a hidden property which captures the connection between the object literal, obj, and its immediate parent in the prototype chain. Almost all objects in JavaScript have a [[Prototype]] property. It is hidden in the sense that it can only be accessed indirectly using either the Object.getPrototypeOf() static method (see line 19), or the .__proto__ property (see line 20), although this latter approach is now deprecated and not recommended for use in production code (we’ll still use .__proto__ in this article as an analog for [[Prototype]]).

So that solves the riddle of the source of hasOwnProperty.

Next, we look at another property, constructor. This property allows us to check the origin of a given object, i.e., how was it created and if it represents data of a particular type. As obj was manually created using object literal syntax, its constructor will be Object. As briefly alluded to earlier, Object is a built-in constructor function that JavaScript uses to create all such objects by setting the [[Prototype]] of the newly-created object to reference the prototype object of Object, Object.prototype.

Furthermore, all objects created using the object literal syntax will inherit the constructor property from the prototype object rather than holding their own copy (see lines 34–35) (unless we manually assign a value to the constructor property of the new object). Indeed, manually setting the value of any property of an object will create a new copy of it on that particular object instance, shadowing any other value stored by an object further up in the prototype chain.

As a final point, note that only functions have a prototype object referenced by the prototype property, so obj will not have a default prototype property (see line 39). We will explore this point further in the next example.

Example 2

Fig 2: Schematic showing how a function literal object, ‘func’, inherits its properties and methods such as ‘call()’ and ‘apply()’ from the ‘Function.prototype’ object which is the base object that all functions inherit from. Note that ‘func’ also has a default ‘prototype’ property that points to its own prototype object, i.e., the object that will be referenced by the ‘[[Prototype]]’ property of all new objects created using ‘new func()’.

Let’s now repeat the analysis using a function literal, func. It’s a fairly simple function that simply logs “hello world!” to the console when invoked.

As was the case with the object literal obj in the previous example, when we defined a new function literal func, JavaScript will create it using a particular constructor function, Function. Furthermore, the newly-created function object will have a [[Prototype]] property that points to Function.prototype from which func can inherit methods and properties such as call() (see line 21) and constructor (see line 15). The code snippet on line 10 confirms that the particular object referenced as the prototype of func is indeed Function.prototype.

Similar to how the Object constructor function had a prototype property that pointed to a prototype object, the constructor Function also has its own prototype object referenced by Function.prototype. On line 11, we further validate that func has been created by the constructor Function using the constructor property.

The primary difference between the object literal obj studied in the previous example and the function literal func is that, now, there is a new prototype property that has been defined on func.

That means func can be also used as a constructor by invoking it using the new keyword (line 26), and this will create a new object, e.g. newObj, whose [[Prototype]] will point back to func.prototype (see lines 27–28).

All object instances created by func will, therefore, inherit from func.prototype. In particular, the constructor property that each instance has access to will point back to the original function func, serving as a marker of how it was created. This can be useful later when we want to investigate, perhaps for debugging purposes, the origin or ‘type’ of a given object as demonstrated on lines 29–30. Thus, in this example, newObj is an instance of type func.

The Function constructor is ‘special’ in that it creates new objects of type ‘function’. Thus, function objects created by the Function constructor are themselves constructors that can create other objects. Indeed, its own prototype object, Function.protoype, is itself a function as can be validated by the code snippet typeof Function.prototype (see lines 33–34) which duly returns “function”.

Interestingly, the Function constructor is itself an instance of its own prototype object, i.e., Object.getPrototypeOf(Function) === Function.prototype returns true (see lines 38–41). Even the constructor Object which we encountered in the previous example, and the Array constructor that we’ll see in the next example, point to Function.prototype as their [[Prototype]] (see lines 46–47).

In fact, all functions in JavaScript, whether they are ordinary functions or constructors, will be ‘descendants’ of Function.prototype, i.e., they will have Function.prototype as a prototype somewhere in their prototype chain and Function.prototype.isPrototypeOf(<someFunction>) always returns true.

Example 3

Fig 3: Schematic of an array literal ‘arr’ and its corresponding constructor ‘Array’. Now, the ‘[[Prototype]]’ of the array object will reference the prototype object of the ‘Array’ constructor, ‘Array.prototype’

With this next example, we have an array literal, arr, with two elements, as well as the length property necessary for all array-like objects. As with the object literal and the function literal, JavaScript uses a built-in constructor to create the new array object behind the scenes. Thus, the [[Prototype]] of arr points to the prototype object of Array, Array.prototype, from which it can inherit the array instance methods such as forEach(), map(), filter(), reduce(), join(), and so on.

In the code snippet above, on line 27, we further confirm that the array object does not have a prototype property that points to any prototype object. Only function objects will have this property by default, and which is used to create a new object when the function is invoked as a constructor using the new keyword.

Finally, what about the [[Prototype]] property of the Array constructor, and the [[Prototype]] property of Array’s prototype object, Array.prototype? What objects do these two properties point to? This will be explored in the next example.

Example 4

Fig 4: Prototype chain of the array literal, ‘arr’, and the ‘Array’ constructor. ‘arr’ inherits from the prototype object of ‘Array’, ‘Array.prototype’, which in turn inherits from the prototype object of ‘Object’, ‘Object.prototype’. On the other hand, ‘Array’ inherits from the prototype object of ‘Function’, ‘Function.prototype’, which in turn also inherits from ‘Object.prototype’.

With this next example, we get the entire picture of the prototype chain of the array literal arr, and that of its constructor, Array.

The arr object inherits from its prototype, Array.prototype which, in turn, inherits from its prototype, Object.prototype. We can verify this relation between the objects by following the links along the prototype chain, i.e., the following the dotted lines from the [[Prototype]] property of arr all the way to Object.prototype. This can be confirmed programmatically as well as shown in lines 7–8 in the code snippet above.

We also see that Object.prototype is the last object in the prototype chain as it does not inherit from any other objects, and its [[Prototype]] explicitly points to null (see line 14). Note the distinction between a property evaluating to null which represents an intentional absence of value versus what JavaScript returns when there is a non-existent property, namely, undefined (see line 15).

That clarifies the entire prototype chain of arr. How about the constructor Array? What does its [[Prototype]] point to?

First, note that all three constructors Array, Object, and Function have a type of ‘function’ (lines 20–22). Already, we can hazard a guess what their prototype object will be as only the Function constructor can create objects of type ‘function’ (see line 31). Indeed, the code on lines 24–26 confirms that all three constructors reference Function.prototype as their prototype.

Furthermore, Function.prototype is an object of type ‘function’ that has its prototype, Object.prototype (see line 34). This rather circular and tightly-coupled relationship between the Object and Function constructors is illustrated in the Fig. 5 below.

A more thorough exploration of these two constructors and the objects that are created by each, namely, function literals and object literals, is provided in this article.

Fig 5: The constructor functions ‘Function’ and ‘Object’ create, respectively, function literals and object literals. Each new instance will inherit methods and properties from the respective prototype object.

--

--