Mind Chasers Inc.
Mind Chasers Inc.

Review of Javascript Objects, Constructors & Prototypes using Browser Debugger

Using a script embedded in the page and the developer console, we review the use of objects, prototypes, and constructors to achieve inheritance.

Overview

This article reviews the use of Javascript objects, prototypes, constructors, and inheritance. Although there is a significant amount of documentation on the Web already about this topic, we have found a good deal of it to be confusing or incomplete. Therefore, we're providing this article as our own reference, which we'll refer to in other articles dealing with web browsers, our library functions, and privacy. The flow and content of this particular article is inspired by the excellent book Javascript Ninja, Second Edition by Resig, Bear, and Maras.

In this article, we take the approach of embedding a commented, non-minimized script within the HTML of this page "static/src/js_tst_objs.js", and we encourage the reader to step through the code line by line in the developer console of their browser in a split screen fashion as shown below in Figure 1. The entire script is also included at the bottom (as HTML) and core snippets of the code are shown throughout the page.

safari web inspector
Figure 1. Safari split screen with Web Inspector / developer console

As you may know, classes were added in ES6 (ECMAScript 2015), but this is just basically window dressing over JavaScript's existing prototype-based inheritance language mechanisms. Also, many popular Javascript libraries don't use the class syntax. Therefore, it's very important to have a firm grasp of these core prototype-related Javascript concepts.

If you plan to debug along while reading this article, go ahead and open your developer console. On Windows & Linux: CTRL+SHIFT+S should work, and on MacOS: COMMAND+OPTION+I should do it. Note that we provide references at the bottom of this article for both Firefox's Debugger and Safari's Web Inspector. These are the two that we use extensively and like very much.

The article and script are divided into three parts:

  1. Basic object creation with literal notation
  2. Object creation with a constructor function
  3. Methodology for inheritance

Core snippets are shown below but make sure to examine, test, and experiment with the other lines of code in the script that provide further introspection and manipuation of the objects we're creating.

Part 1: Basic object creation with literal notation

The first thing we do is create an object obj1 with literal notation.

let obj1 = {
	p1: 1,
	p2: function(){},
	p3: {}
}

Create a second object obj2 to work with.

let obj2= {
	p4: 'hi'
};

Note that both of these objects were constructed using the core Object constructor and inherit the properties and methods of the Object.prototype object (e.g, isPrototypeOf). See Figure 2 below for an inspection of obj2.

obj1 properties
Figure 2. Inspect obj2 using Safari Web Inspector

obj1 and obj2 both have an implicit prototype property that we denote [[prototype]] in the object diagrams below. We can set an object's prototype directly by using Object.setPrototypeOf(), which is a method of the Object constructor:

// obj2 inherits obj1 properties through prototype 
Object.setPrototypeOf(obj2, obj1);

After performing the step above, inspect obj2 in your developer console and see how obj2 now has the properties in obj1.

Before we go onto the next part of this article, step through the rest of part 1 in the script and make sure you understand the following:

  • Basic operations on object properties like assigning properties dynamically and deleting properties
  • Use instanceof to test whether a particular function's prototype is in the prototype chain of an object
  • Use in to test membership of a property in an object

Part 2: A Constructor function and object creation

As shown below, we create an object with function constructor ObjCreator().

Note that every function constructor we define has a prototype object that initially references an empty object. This prototype object is automatically set as the prototype of the objects created with the function, and the prototype gains a constructor property that points back to the function, which the object can use to identify its constructor. This relationship is shown in Figure 3.

In this example, we also add a new mult method to our prototype, which obj3 can access through its prototype chain.

function ObjCreator() {
	this.p6 = "hi";
}
let obj3 = new ObjCreator();
ObjCreator.prototype.mult = function(a,b) { return a*b; }
obj1 properties
Figure 3. obj3 relationship with its prototype and constructor

Figures 4 and 5 below show the inspection of obj3 and its prototype chain using both Safari Web Inspector and Firefox Debugger.

obj1 properties
Figure 4. Safari: Inspect obj3
obj1 properties
Figure 5. Firefox: Inspect obj3

Part 3: Methodology for inheritance

In this section we provide an example of inheritance by having the child object inherit the properties of its parent object. child is constructed from function ChildObjCreator(), and the parent object is constructed from function ObjCreator().

Below we use an instance of the parent object as the child constructor's prototype, and this will establish the prototype chain and inheritance that we seek. child will have access to its parent's properties and methods all the way up the prototype chain through Object.prototype.

	ChildObjCreator.prototype = new ObjCreator();
	let child = new ChildObjCreator();

As recommended in Javascript Ninja, Second Edition, let's fix up the child constructor, so we can determine programmatically which function constructed the child object. Otherwise, it will point to ObjCreator.

	Object.defineProperty(ChildObjCreator.prototype, "constructor", {
		enumerable: false,
		value: ChildObjCreator,
		writable: true
		});

Figure 6 below depicts the relationship between the objects, constructors, and prototypes we have created.

obj1 properties
Figure 6. child, parent relationship

Lastly, Figure 7 shows an inspection of the child object using Safari Web Inspector.

obj1 properties
Figure 7. Safari: Inspect child object

There are a number of corner cases to be aware of when working with Javascript prototypes, such as what happens when a constructor's prototype is set to a new object after the constructor creates an object. We plan to expand this document in the future to cover such cases.

Javascript, js_tst_objs.js
/* 
 * Copyright 2019 Mind Chasers Inc.
 * file: js_tst_obj.js
 */

function ObjCreator() {
	this.p6 = 'hi';
}

function ChildObjCreator() {
	this.p7 = 'there';
}

window.addEventListener("load",function(e) {
	
	// PART 1: create local object obj1 with literal notation
	let obj1 = {
		p1: 1,
		p2: function(){},
		p3: {}
	}
		
	//  All objects inherit methods and properties from Object.prototype
	console.log(obj1 instanceof Object);	// true
	console.log(obj1.hasOwnProperty('p1'));	// true
	console.log(obj1.hasOwnProperty('p4')); // false
	
	// create a second object to work with
	let obj2= {
		p4: 'hi'
	};
	
	// basic object property manipulation examples
	obj2.p5 = 1.63;					// add a new property dynamically 
	delete obj1.p3;					// remove the property
	console.log(Object.keys(obj1)); // returns array of object's enumerable properties
	
	
	// obj2 inherits obj1 properties through its implicit prototype 
	// See Figure 1
	Object.setPrototypeOf(obj2, obj1);

	console.log('p1' in obj2);		// test for membership of a property in an object
	console.log('obj2:', obj2);
	
	if (obj2.prototype === undefined ) {
		console.log("we can't access the object's prototype property directly"	);
	}
	
	// PART 2: Create object with Constructor
	let obj3 = new ObjCreator();
	ObjCreator.prototype.mult = function(a,b) { return a*b; }
	
	console.log(obj3.mult(2,3));
	console.log(obj3.constructor === ObjCreator);
	console.log(obj3.constructor.prototype instanceof Object);
	console.log('obj3', obj3);
	
	
	// PART 3: Inheritance
	ChildObjCreator.prototype = new ObjCreator();
	let child = new ChildObjCreator();
	console.log(child instanceof ChildObjCreator);	// true
	console.log(child instanceof ObjCreator);		// true
	console.log('p6' in child);		// true
	
	// by overwriting the prototype, we lost the connection of our child.constructor and ChildObjCreator
	console.log(child.constructor === ChildObjCreator);	// false
	
	Object.defineProperty(ChildObjCreator.prototype, "constructor", {
		enumerable: false,
		value: ChildObjCreator,
		writable: true
		});
	
	console.log(child.constructor === ChildObjCreator);	// true
	console.log(child instanceof ObjCreator);		// true
});

References

Didn't find an answer to your question? Post your issue below or in our new FORUM, and we'll try our best to help you find a solution.

And please note that we update our site daily with new content related to our open source approach to network security and system design. If you would like to be notified about these changes, then please follow us on Twitter and join our mailing list.

Related articles on this site:

share
subscribe to mailing list:

Please help us improve this article by adding your comment or question:

your email address will be kept private
authenticate with a 3rd party for enhanced features, such as image upload
previous month
next month
Su
Mo
Tu
Wd
Th
Fr
Sa
loading