OOP in JavaScript 1686296215
OOP in JavaScript 1686296215
Section 1: Introduction
Before we get into OOP with JavaScript, let's review some JavaScript
fundamentals. If you're already comfortable with these concepts, feel
free to move to the next section.
2.1 Syntax
let x = 10;
const y = 20;
In addition to these, JavaScript has the Object data type which can
store collections of data.
function sayHello() {
console.log("Hello, world!");
}
if (x > y) {
console.log("x is greater than y");
} else {
console.log("x is not greater than y");
}
let dog = {
name: "Spot",
breed: "Dalmatian",
age: 3,
bark: function() {
console.log("Woof!");
}
};
Using Object.create():
dog.bark = function() {
console.log("Woof!");
};
Object.create(proto, [propertiesObject])
You can access properties and methods on an object using dot notation
or bracket notation:
And you can delete properties from an object using the delete keyword:
delete dog.age;
console.log(dog.age); // outputs: undefined
You can then create a new Dog object using the new keyword:
let animal = {
species: "animal",
describe: function() {
return `This is a ${this.species}.`;
}
};
function Animal(species) {
this.species = species;
}
Animal.prototype.describe = function() {
return `This is a ${this.species}.`;
};
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
return `${this.name} says woof!`;
};
Then we add a bark method to the Dog prototype. This method is specific
to Dog instances and is not shared by Animal instances.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
area() {
return this.height * this.width;
}
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
Section 7: Encapsulation
Here is an example:
class Car {
constructor(make, model) {
this._make = make;
this._model = model;
this._odometer = 0;
}
get make() {
return this._make;
}
get model() {
return this._model;
}
drive(distance) {
this._odometer += distance;
}
readOdometer() {
return this._odometer;
}
}
Keep in mind that this isn't true privacy. The properties can still be
accessed and modified directly, and the underscore is just a convention
to signal that they shouldn't be. ES6 introduced a new feature,
JavaScript #private fields, to make properties truly private.
class BankAccount {
#balance;
constructor(initialDeposit) {
this.#balance = initialDeposit;
}
deposit(amount) {
this.#balance += amount;
}
withdraw(amount) {
if (this.#balance >= amount) {
this.#balance -= amount;
getBalance() {
return this.#balance;
}
}
As you can see in this example, attempting to access the #balance field
directly from outside the class results in a syntax error. This ensures
that the #balance field can only be accessed or modified through the
deposit, withdraw, and getBalance methods.
Section 8: Polymorphism
Here's an example:
In this example, the Dog and Cat classes override the makeSound method
of the Animal class. When makeSound is called on a Dog or Cat object,
the overridden method in the respective child class is executed.
Sometimes, you might want to execute the parent class's method before
or after executing additional code in the child class's method. You can
do this using the super keyword.
Here's an example:
Section 9: Abstraction
class Vehicle {
constructor(name, type) {
this.name = name;
this.type = type;
}
start() {
return `${this.name} engine started`;
}
}
class BankAccount {
constructor(balance = 0) {
this.balance = balance;
}
deposit(amount) {
this.balance += amount;
return this.balance;
}
withdraw(amount) {
if (amount > this.balance) {
return "Insufficient funds";
} else {
this.balance -= amount;
return this.balance;
}
}
}
Composition and inheritance are two ways to reuse code across objects
in JavaScript.
const canEat = {
eat: function() {
console.log("Eating");
}
};
const canWalk = {
walk: function() {
console.log("Walking");
}
};
In this example, we're composing a person object from the canEat and
canWalk objects, rather than inheriting from a parent class.
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
In each case, we're achieving the same end result: we're creating a
person object with a name and age. The method you choose to create
objects really depends on your specific needs and the conventions of
your codebase.
// mathFunctions.js
export function add(x, y) {
return x + y;
}
// MyModule.js
export default function() { console.log("I'm the default export!"); }
You can use the import keyword to import functions, objects, or values
that were exported from another module.
// main.js
import { add, subtract } from './mathFunctions.js';
console.log(add(2, 2)); // 4
console.log(subtract(2, 2)); // 0
// main.js
import myDefaultFunction from './MyModule.js';