Summary - Javascript was not built to be used as an Object Oriented Programming language. So at first, using OOP in JS was weird and awkward. But it has come a long way and its evolution is explained in detail here.
Some key terms related to this article are encapsulation, namespace, abstraction, constructor functions, instantiation, inheritance, this, polymorphism, delegation, the prototype property and object literals.
The Basics
Constructors
All functions are objects in JS, but a function that creates an object, which does NOT return anything, and is meant to be used as an Object, is a constructor function. In order to differentiate a constructor function from a regular function, constructor function names start with a capital letter. They don't need to start with a capital letter, but it is the standard.
What does a constructor do?
It constructs the object, of course. It does this by defining the properties to be used in the object. Those properties can be any JS type, including functions. A function defined by a constructor is called a method. Defining all of those properties within an object is called encapsulation.
Instantiation
When you create a new object from the constructor function you are 'instantiating' a new object. Whenever you come across the word 'new' in JS you should expect that you are instantiating an object which will come from a parent object, which will inherit all of the properties defined by the parent object. When one object takes on the properties of another object, that is called polymorphism.
Inheritance
When you create the new Object from the parent object, the new object inherits all of the properties of the parent object, as well as any properties its parent object inherited from its parent object, etc.
Every object, no matter how it is created in JS, is automatically a child object of the window, document, or global object. Those objects have a long list of built-in properties and every new object that is created inherits those properties.
This way, you do not have to create all these basic properties for every object.
this
In a constructor function, the 'this' keyword refers to the object that is calling/using the constructor function.
A way to think about the 'this' keyword is like saying 'the instantiated object can use this property'. Without the 'this' property, the instantiated object will not have access to that property (however, functions created within the object will have access to the property, and that technique is used to keep properties private, but that topic will be discussed in a different article).
Note - JS OOP is not as complicated as it is made out to be. All the definitions don't really matter unless you are being tested in a formal classroom. In the real world, you simply need to know how things work and it is very easy to understand how OOP works in JS if you simply look at some examples.
Basic OOP Example
function Greetings(name){
this.name = name
this.morning = () => this.name + ', good morning!'
this.afternoon = () => this.name + ', good afternoon!'
this.night = () => this.name + ', good night!'
}
let person1 = new Greetings('Tom')
let person2 = new Greetings('Mary')
person1.morning() === 'Tom, good morning!'
person2.night() === 'Mary, good night!'
person1.name === 'Tom'
Javascript's Prototype Chain
In the basic OOP example above, every time a new object is instantiated all of the properties of the parent object are being redefined and saved in memory as part of the newly instantiated object. To correct that terrible waste of resources a new technique was created which uses a mix of OOP Basic and prototype chaining.
In JS every object has a built-in prototype property. The prototype property is an object of properties. Every child of an object has access to every parent's prototype property through inheritance.
Therefore you can use the prototype to add new properties that are equal to function expressions, instead of adding those methods in the constructor function. Then, when you instantiate a new object, the new object won't make copies of the those expressions, but they will have access to them.
Why not add all properties to the prototype object?
Because a function is an object you can use the 'this' keyword inside it in order to reference the object that is calling it. Basic properties for strings, numbers, booleans and arrays are not objects, thus have no 'this', thus they must remain within the object constructor in order for the new object to understand that 'this' is referencing the object that called it.
function Greetings2(name){
this.name = name
}
Greetings2.prototype.morning = function() {
return this.name + ', good morning! '
}
Greetings2.prototype.afternoon = function() {
return this.name + ', good afternoon!'
}
Greetings2.prototype.night = function() {
return this.name + ', good night!'
}
let person3 = new Greetings2('Ed')
let person4 = new Greetings2('Dani')
person3.morning()
person4.night()
person3.name
Note that because prototype properties are properties, they must equal a function expression. They cannot use ES6 arrow functions, nor can they use function declarations, like this...
Greetings2.prototype.morning = () => this.name + ', good morning!'
or
Greetings2.prototype.morning() {
return this.name + ', good morning!'
}
Using class to create an Object
JS introduced the class keyword in ES5 and it has become the new normal way to create objects in javascript. Prototyping was formerly the most popular way to create objects and can probably be found in a great deal of code created before 2016, but since then, class has taken over.
The concept of class in JS is to mimic how classes are created in Object Oriented Programming languages like C++ and Java. We say mimic because technically classes in JS are converted behind the scenes into the prototypal object method, so it is still not technically a class the way Java and C++ use them, however, it is much easier to read and work with and programmers going one from language to another should easily be able to understand the other.
class is used extensively in most JS frameworks like Angular and React, so it is vital to understand how they work, and if you understand everything so far, this should be a breeze.
The main difference is that the constructor, which accepts the initial parameters is literally defined within the class. Also, the methods do not need to be started with the 'this' keyword, but they do need to use 'this' if they are referencing one of the passed in parameters in the constructor.
class example
class Greetings3 {
constructor(name){
this.name = name
}
morning() {
return this.name + ', good morning!'
}
afternoon() {
return this.name + ', good afternoon!'
}
night(){
return this.name + ', good night!'
}
}
let ralph = new Schedule('Ralph')
let vanessa = new Schedule('Vanessa')
ralph.morning()
vanessa.night()
Note on how to write functions in a class... you should not use arrow functions in your class because technically you are writing a function definition on the fly and arrow functions do not get added to the object's prototype. This may be confusing because if you are working in Node in order to create React apps you will likely use arrow functions, however, understand that this only works because you will be using Babel, which is an interpretation library that converts ES6 code into basic JS code.
Topics not covered here, but still needed to fully understand OOP in JS are namespacing, delegation, private and public properties and functions, and a few other things. Quickly, here is a bit about namespace and delegation.
Namespace
Namespacing is when you create a new constructor function but you say that it 'extends' a parent constructor function. Which means it will have available to it all the properties of the parent object, but will also add properties specific to its 'name'. For instance if you have a Person object, then create a new object that is a Teacher. So the teacher is also a person, but it has specific properties that say, a Student might have. This is not an advanced, or difficult to understand topic, but you have to know how basic objects are created first so we will discuss this in another article.
Delegation
If you create an object that is meant to hold a bunch of functions in order for many other objects to use, you are delegating functions.