A Gentle Introduction to Object-Oriented Programming in JavaScript

Understanding JavaScript’s object oriented programming system can be difficult, especially if your background is in a language that uses a class-based system such as Python, Java, or C++. I can remember being incredibly frustrated trying to understand how objects, prototypes and constructor functions work together when I first started out in JavaScript. This blog post is meant to be the guide that I never had back then. In it, I’ll introduce the key concepts in JavaScript’s prototype-based OOP system, hopefully addressing some of the confusion surrounding it.

Objects in JavaScript

Javascript has six primitive types as specified in ECMAScript 2015: undefined, null, boolean, number, symbol, and string. Everything else is an object. Objects are collection of key-value pairs called properties. An object can be created by enclosing colon-separated keys and values in curly braces, and is called an object literal. Here’s an example:

In the above example, we create an object and assign it to a variable called dog. The object has three properties, name (a string), age (a number), and bark (a function). In order to access an object’s properties, you can use either dot or square bracket syntax:

The Prototype

JavaScript’s OOP system is prototype based. This means that every object has another object associated with it called its prototype. A prototype object in turn has its own prototype object, which also has its own prototype object, and so on. This pattern continues until the prototype of an object is null. This “chain” of objects, ending in null, is called the prototype chain. For example, say we have an object that represents an employee. Its prototype chain may look something like this:

image00

In this example, the human object is the prototype of the employee object and the animal object is the prototype of the human object. The animal object’s prototype is null, ending the prototype chain.

When we try to access a property on an object, JavaScript will first look in the object for the property. If it isn’t found, it will search its prototype, and then its prototype’s prototype, and so on, returning the property once it is found, or returning undefined if it reaches the end of the chain. For example, say we have the employee object described above and we try to access employee.diet. Here’s a diagram representing what may happen:

image01

In the above example, we try to access employee.diet. The employee object does not have a property called diet, so it looks in human, its prototype object. The human object does not have a property called diet, so it looks in animal, its prototype object. The animal object does contain a property called diet whose value is ’omnivore’, so JavaScript returns it.

The way that JavaScript searches through the prototype chain works much like inheritance in class-based languages. It is, in fact, inheritance, albeit prototype-based and not class-based inheritance. One crucial thing to note about this type of inheritance however, is that prototypes are not the same thing as classes. Prototypes are objects, while classes serve as blueprints for objects.

Working With Prototypes

In most browsers, all objects in JavaScript have a __proto__ property that is a reference to the object’s prototype. This property originated in Firefox as a quick and dirty hack, and was eventually implemented in other browsers due to its popularity. It was only recently that it was standardized in ES2015 in order to ensure compatibility. Due to this, it’s bad practice to use __proto__ in production code. I’m going to use it for demonstration purposes however, as it makes things easy to explain. If you need to access an object’s prototype, you should use the more standard Object.getPrototypeOf(obj) function instead.

In order to create a new object with another as its prototype, you can use the Object.create(prototype) function. Let’s create an object, and make it the prototype object of another object.

Figuring out what’s going on here shouldn’t be too hard if you understood the previous section. We create an object called obj1 using the object literal syntax, which contains one property, property1. We then create another object called obj2, whose prototype object is obj1 and verify that the prototype of obj2 is obj1, which it is. When we try to access obj2.property1, JavaScript goes up the prototype chain until it finds property1 in obj1, and returns it.

You may be wondering what obj1’s prototype is. The answer is Object.prototype. All objects created using the object literal syntax have Object.prototype set as their prototype object. The reason for this will be explained in the next section.

Constructor Functions

So far, we’ve only used JavaScript’s object literal syntax to create objects. Another way to create objects however, is through the use of a constructor function. Constructor functions can be difficult to understand, but are very commonly used in JavaScript, and a complete understanding of them is important.

In JavaScript, a constructor function is simply a function that is invoked with the new keyword. When a function is invoked with new, a new empty object is created and the constructor function is executed, with the this keyword acting as a reference to the new object within the constructor function. When the constructor function finishes executing, the object is returned. This allows the programmer to write a function that sets up and returns a new object, much like a constructor in a class-based language.

As an example, let’s create a constructor function to create objects representing students:

In this example, we create a function called “Student” that takes two arguments, name and grade. We invoke the function as a constructor function by using the “new” keyword. The “Student” function then creates a new object, assigns its name and grade properties to the value of the name and grade arguments passed to it, and returns the new object. Note that there is no need to use the return keyword within the Student function as functions invoked with the new operator will return a new object without the use of a return statement.

The Function.prototype Property

All functions in JavaScript have a prototype property. This confusingly named property is not actually the prototype of the function object (__proto__). The prototype property of a function is the object that will be set as the prototype of objects created by the function when it is invoked with the new operator. So in the example in the previous section, the value of student1.__proto__ is Student.prototype.

You can put functions and data into a constructor function’s prototype property to add common functionality to all objects created by that constructor function. For instance, we could add a sayHi function to Student.prototype like so:

All functions in JavaScript have a prototype property. This confusingly named property is not actually the prototype of the function object (proto). The prototype property of a function is the object that will be set as the prototype of objects created by the function when it is invoked with the new operator. So in the example above, the value of student1.__proto__ is Student.prototype.

You can put functions and data into a constructor function’s prototype property to add common functionality to all objects created by that constructor function. For instance, we could add a sayHi function to Student.prototype like so:

In this example, all objects created using the Student constructor function would have access to the sayHi function in Student.prototype because all objects created using the Student constructor function would have Student.prototype set as their prototype and would therefore inherit the sayHi function.

JavaScript has several constructor functions built into the language and available for your use. For instance, you can use new Array() to create a new array, new Date() to create a new date and new Object() to create a new empty object.

Remember how two sections ago, I mentioned that the prototype of objects created using the object literal syntax will be set to Object.prototype? Creating a new object through the use of new Object() is functionally equivalent to using the object literal syntax {}, both will result in an empty object being created. This is why objects created through the use of object literal syntax will have Object.prototype set as their prototype.

When you create a new constructor function, its prototype property will have a prototype of Object.prototype (unless you set it to something else). Object.prototype has a prototype of null. Here’s a diagram to illustrate this, using our student example from above.

image02

Conclusion

Hopefully by this point you have a good general understanding of the basic concepts in JavaScript’s OOP system. While it can be hard to wrap your head around prototype-based OOP initially, a thorough understanding of the concepts discussed in this blog post is necessary for anybody who wants to work with JavaScript.

Further Reading

About the Author
Rhys Rustad-Elliott

Rhys Rustad-Elliott is a High School Summer Technical Student on the Publishing team. He is entering first year at The University of Toronto Scarborough, where he will be studying Computer Science. In his spare time, he enjoys working on technical projects, as well as curling, running, and playing disc golf.