Object-oriented JavaScript
JavaScript is a powerful, flexible language that allows for multiple programming paradigms, including procedural, functional and Object-oriented Programming (OOP). OOP is a style of coding that allows you to create and interact with objects in a structured way. In this cheat sheet, we'll cover key concepts of Object-oriented JavaScript to guide you in applying these techniques to your programming tasks.
OOP Key Principles
Object-oriented programming revolves around the concepts of objects and classes. The fundamental principles guiding OOP are Encapsulation, Inheritance, Polymorphism, and Abstraction.
Here's a basic example demonstrating encapsulation:
class Car {
constructor(make, model) {
this.make = make;
this.model = model;
}
displayCar() {
return `${this.make} ${this.model}`;
}
}
let myCar = new Car("Toyota", "Corolla");
console.log(myCar.displayCar()); // Outputs: "Toyota Corolla"
In this example, a class Car
is defined with a constructor and a method displayCar()
. This showcases encapsulation, where data (properties) and methods are wrapped inside a class.
Creating Classes
In JavaScript, classes are a template for creating objects. They encapsulate data with code to manipulate that data.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
The Animal
class is a basic structure for creating objects with a name property and a speak method. The constructor
method is a special method for creating and initializing an object.
Creating Objects
Objects are instances of a class. Once a class is defined, we can instantiate new objects from it.
let dog = new Animal('Dog');
dog.speak(); // Outputs: "Dog makes a noise."
Here, dog
is an object of the Animal
class, created using the new
keyword.
Working with Classes
Extends
The extends
keyword in JavaScript is used to create a class that is a child of another class.
class Dog extends Animal {
speak() {
console.log(`${this.name} barks.`);
}
}
let dog = new Dog('Rover');
dog.speak(); // Outputs: "Rover barks."
In the example above, Dog
is a subclass (or child class) of Animal
and it overrides the speak
method of the parent class.
Super
The super
keyword is used to call corresponding methods of the parent class. This is particularly useful in the case of the constructor of the child class.
class Dog extends Animal {
speak() {
super.speak();
console.log(`${this.name} barks.`);
}
}
let dog = new Dog('Rover');
dog.speak(); // Outputs: "Rover makes a noise. Rover barks."
In this code, super.speak()
is called before Dog
's speak()
method, which results in both methods running when dog.speak()
is called.
Prototypes
Prototypes and Prototype Chains
In JavaScript, each object has a link to a prototype object. When trying to access a property that does not exist in an object, JavaScript tries to find this property in the prototype of this object.
let animal = {
kind: 'animal',
getKind: function() {
return this.kind;
}
}
let dog = Object.create(animal);
dog.kind = 'dog';
console.log(dog.getKind()); // Outputs: "dog"
In the above example, the dog
object is linked to the animal
object. The animal
object is now the prototype of dog
.
Inheriting Properties and Methods using Prototypes
JavaScript objects can inherit properties and methods from a prototype.
let animal = {
makesSound: function() {
console.log('noise...');
}
}
let dog = Object.create(animal);
dog.makesSound(); // Outputs: "noise..."
Here, dog
object inherits the makesSound
method from its prototype animal
.
Constructors and Prototypes
Constructors vs. Prototypes
A constructor is a function that is used to instantiate a new object, whereas the prototype is an object instance that is attached to every object created from the constructor.
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(`${this.name} makes a noise.`);
}
let dog = new Animal('Dog');
dog.speak(); // Outputs: "Dog makes a noise."
Here, the constructor Animal
has a speak
method attached to its prototype, and every object created using new Animal
has access to this method.
Modifying Prototypes
Prototypes can be modified to add new properties or methods, which will then be accessible to all objects of that type.
Animal.prototype.eat = function() {
console.log(`${this.name} eats.`);
}
dog.eat(); // Outputs: "Dog eats."
In this example, the eat
method is added to the Animal
prototype and it is immediately available to all instances of Animal
, including dog
.
OOP JavaScript Patterns
Singleton Pattern
The Singleton pattern restricts a class from instantiating multiple objects. It is used where only a single instance of a class is required to control actions.
let Singleton = (function () {
let instance;
function createInstance() {
return { text: "I am an instance" };
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
let instance1 = Singleton.getInstance();
let instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // Outputs: true
Factory Pattern
The Factory pattern is a creational pattern that provides a way to create objects in a super-class and allows subclasses to alter the type of objects that will be created.
class AnimalFactory {
createAnimal(type) {
let animal;
if (type === 'Dog') {
animal = new Dog();
} else if (type === 'Cat') {
animal = new Cat();
}
animal.type = type;
return animal;
}
}
Module Pattern
The Module pattern is used to create private and public encapsulation for classes in JavaScript.
let myModule = (function () {
let privateVariable = 'private';
function privateMethod() {
return 'This is a private method';
}
return {
publicMethod: function() {
return 'Public can see me!' + privateMethod();
}
};
})();
console.log(myModule.publicMethod()); // Outputs: "Public can see me! This is a private method"
Observer Pattern
The Observer pattern offers a subscription model in which objects subscribe to an event and get notified when the event occurs.
class Observer {
constructor() {
this.observers = [];
}
subscribe(f) {
this.observers.push(f);
}
unsubscribe(f) {
this.observers = this.observers
.filter(subscriber => subscriber !== f);
}
notify(data) {
this.observers.forEach(observer => observer(data));
}
}
Unit Testing for OOP
Unit testing in OOP JavaScript typically involves testing individual methods of a class. One common unit testing framework used in JavaScript is Jest.
const Animal = require('./Animal'); // Assume you have Animal class in Animal.js
test('check if the animal speaks', () => {
let testAnimal = new Animal('Test');
expect(testAnimal.speak()).toBe('Test makes a noise.');
});
In this code, a unit test is written for the speak
method of the Animal
class. Jest provides the test
and expect
functions to facilitate testing.