OOP
Today, many popular programming languages (such as Java, JavaScript, C#, C++, Python, PHP, Ruby and Objective-C) support OOP. Object-oriented programming (OOP) is a programming paradigm that uses abstraction to create models based on the real world. OOP uses several techniques from previously established paradigms, including modularity, polymorphism, and encapsulation. OOP refers to using self-contained pieces of code to develop applications.
- We call these self-contained pieces of code objects, better known as Classes in most OOP programming languages and Functions in JavaScript.
- We use objects as building blocks for our applications. Building applications with objects allows us to adopt some valuable techniques, namely, Inheritance (objects can inherit features from other objects), Polymorphism (objects can share the same interface—how they are accessed and used—while their underlying implementation of the interface may differ) and Encapsulation (each object is responsible for specific tasks).
Terminology
Namespace
A container which lets developers bundle all functionality under a unique, application-specific name.
Class
Defines the object's characteristics. A class is a template definition of an object's properties and methods.
Object
An instance of a class.
Property
An object characteristic, such as color.
Method
An object capability, such as walk. It is a subroutine or function associated with a class.
Constructor
A method called at the moment an object is instantiated. It usually has the same name as the class containing it.
Inheritance
A class can inherit characteristics from another class.
Encapsulation
A method of bundling the data and methods that use the data.
Abstraction
The conjunction of an object's complex inheritance, methods, and properties must adequately reflect a reality model.
Polymorphism
Poly means "many" and morphism means "forms". Different classes might define the same method or property.
JavaScript OOP
Namespace
A namespace is a container which allows developers to bundle up functionality under a unique, application-specific name. In JavaScript a namespace is just another object containing methods, properties, and objects.
The idea behind creating a namespace in JavaScript is simple: create one global object, and all variables, methods, and functions become properties of that object. Use of namespaces also reduces the chance of name conflicts in an application, since each application's objects are properties of an application-defined global object.
Let's create a global object:
// global namespace
var SELFLEARNINGS = SELFLEARNINGS || {};
In the above code sample, we first checked whether SELFLEARNINGS is already defined (either in same file or in another file). If yes, then use the existing SELFLEARNINGS global object, otherwise create an empty object called SELFLEARNINGS which will encapsulate methods, functions, variables, and objects.
We can also create sub-namespaces:
// sub namespace
SELFLEARNINGS.event = {};
Custom objects
The class
JavaScript contains no class statement, such as is found in C++ or Java. This is sometimes confusing for programmers who use languages with a class statement. Instead, JavaScript uses functions as classes. Defining a class is as easy as defining a function. In the example below we define a new class called SelfLearning.
var SelfLearnings = function () {};
The Object (Class Instance)
To create a new instance of an object obj we use the statement new obj, assigning the result (which is of type obj) to a variable to access it later.
In the example below we define a class named SelfLearning and we create two instances (selflearning1 and selflearning2).
function SelfLearning() { }
var selflearning1 = new SelfLearning();
var selflearning2 = new SelfLearning();
Note: Please see Object.create() for a new, additional, instantiation method that creates an uninitialized instance.
The constructor
The constructor is called at the moment of instantiation (the moment when the object instance is created). The constructor is a method of the class. In JavaScript the function serves as the constructor of the object, therefore there is no need to explicitly define a constructor method. Every action declared in the class gets executed at the time of instantiation.
The constructor is used to set the object's properties or to call methods to prepare the object for use. Adding class methods and their definitions occurs using a different syntax described later in this article.
In the example below, the constructor of the class SelfLearning logs a message when a SelfLearning is instantiated.
function SelfLearning() {
alert('SelfLearning instantiated');
}
var selflearning1 = new SelfLearning();
var selflearning2 = new SelfLearning();
The Property (object attribute)
Properties are variables contained in the class; every instance of the object has those properties. Properties should be set in the prototype property of the class (function) so that inheritance works correctly.
Working with properties from within the class is done by the keyword this, which refers to the current object. Accessing (reading or writing) a property outside of the class is done with the syntax: InstanceName.Property; this is the same syntax used by C++, Java, and a number of other languages. (Inside the class the syntax this.Property is used to get or set the property’s value.)
In the example below we define the gender property for the SelfLearning class and we define it at instantiation.
function SelfLearning(type) {
this.type = type;
alert('SelfLearning instantiated');
}
var selflearning1 = new SelfLearning('Javascript');
var selflearning2 = new SelfLearning('Java');
//display the selflearning1 type
alert('selflearning1 is a ' + selflearning1.type); // selflearning1 is a Javascript
Encapsulation
It refers to the practice of hiding the implementation details of an object from the outside world, and only exposing a public interface for interacting with that object.
An example of encapsulation in JavaScript is creating a SelfLearning object that has a private name property and a public getName() method:
function SelfLearning(type) {
let privateTypeName = type;
this.getType = function() {
return privateTypeName;
};
}
const selflearning = new SelfLearning("Javascript");
console.log(selflearning.getType()); // "Javascript"
console.log(selflearning.privateTypeName); // undefined (private property)
In this example, the type property is defined as a private variable within the SelfLearning function, and can only be accessed through the public getType() method. Attempting to directly access the privateTypeName property will return undefined.
Inheritance
Inheritance allows objects to inherit properties and methods from a parent object. This can be achieved through the use of the prototype property.
For example, let's say we have a SelfLearning object and we want to create a Category object that inherits from the SelfLearning object. We can do this by setting the Category object's prototype to an instance of the SelfLearning object:
function SelfLearning(type, book) {
this.type = type;
this.book = book;
}
SelfLearning.prototype.getTypeName = function() {
return this.type;
};
function Category(type, book, major) {
SelfLearning.call(this, type, book);
this.major = major;
}
Category.prototype = Object.create(SelfLearning.prototype);
Category.prototype.constructor = Category;
const category = new Category("Javascript", "JavaScript: The Good Parts", "Computer Science");
console.log(category.getName()); // "Javascript" (inherited from SelfLearning)
console.log(category.major); // "Computer Science" (unique to Category)
In this example, the Category object inherits the name and age properties and the getName() method from the SelfLearning object. Additionally, it has its own unique major property.
It is important to note that Object.create(SelfLearning.prototype) creates a new object that inherits directly from SelfLearning.prototype. Also Student.prototype.constructor = Student; is to set the correct constructor for the new object.
This is just a simple example of inheritance in JavaScript, and it can be used to create more complex object hierarchies.
Abstraction
Abstraction refers to the process of hiding the implementation details of a function or an object while providing a simplified interface to interact with it. This allows for greater flexibility and modularity in code, as well as the ability to change the implementation without affecting the rest of the codebase.
For example, consider a simple object that represents a car. It has properties such as make, model, and year, and methods such as start and stop. We can create an abstraction for this object by creating a simple interface that only exposes the methods we want to use, and hides the implementation details.
//Car object
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
this.start = function() {
console.log("Car started");
}
this.stop = function() {
console.log("Car stopped");
}
}
//Car interface
var car = new Car("Toyota", "Camry", 2020);
car.start(); //outputs "Car started"
car.stop(); //outputs "Car stopped"
In this example, the user of the car object only needs to know about the start and stop methods, and does not need to know about the make, model, and year properties. This allows for greater flexibility in the implementation of the car object, as the properties and methods can be changed without affecting the code that uses the car interface.
In addition, abstraction also allows for better code organization and easier code maintenance. By keeping the implementation details separate from the interface, developers can make changes to the implementation without having to search through the entire codebase for references to the implementation.
Overall, abstraction is an important concept in programming and can be used to make code more flexible, modular, and maintainable.
Polymorphism
Polymorphism is one of the four fundamental principles of object-oriented programming (OOP), along with encapsulation, inheritance, and abstraction. It allows objects of different classes to be treated as objects of a common superclass or interface.
In JavaScript, polymorphism is achieved through the use of prototypes and prototype inheritance.
For example, let's consider two classes: "Animal" and "Cat". The "Animals" class has a method called "speak" which simply logs "Animal make noise.". The "Cat" class inherits from the "Animals" class and overrides the "speak" method to log "Meow!".
class Animals {
speak() {
console.log("Animals make noise.");
}
}
class Cat extends Animals {
speak() {
console.log("Meow!");
}
}
let cat = new Cat();
let animal = new Animals();
cat.speak(); // Output: "Meow!"
animal.speak(); // Output: "Animals make noise."
In the above example, the "speak" method is polymorphic, because it can be used by objects of different classes (Animals and Cat) and will produce different results depending on the class of the object.
Another way to achieve polymorphism in javascript is by using interfaces. An interface defines a contract for objects, specifying the methods and properties that they must implement.
interface Speakable {
speak(): void;
}
class Dog implements Speakable {
speak() {
console.log("Woof!");
}
}
class Parrot implements Speakable {
speak() {
console.log("Hello World!");
}
}
let dog = new Dog();
let parrot = new Parrot();
dog.speak(); // Output: "Woof!"
parrot.speak(); // Output: "Hello World!"
In the above example, the classes "Dog" and "Parrot" implement the "Speakable" interface, which means that they must implement the "speak" method. This allows us to treat them as a common type and use the "speak" method polymorphically.
In conclusion, Polymorphism is a powerful concept in OOP that allows objects of different classes to be treated as objects of a common type. In javascript, it can be achieved through the use of prototypes, inheritance, and interfaces.