JavaScript is different from many other programming languages when it comes to Object-Oriented Programming (OOP). Instead of using classes as its foundation, JavaScript uses prototypes. ES6 introduced classes, but they are just a cleaner way of working with prototypes.
In this blog, I'll explain the topics I learned today.
1. What is a Prototype?
A prototype is a shared object that stores common methods and properties.
Instead of creating a separate copy of the same method for every object, JavaScript stores it once in the prototype, and all objects created from the same constructor or class share it.
Example
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
console.log(Hello, I'm ${this.name});
};
const p1 = new Person("Harshith");
const p2 = new Person("Rahul");
p1.greet();
p2.greet();
Here, both p1 and p2 use the same greet() method from Person.prototype.
This saves memory because JavaScript doesn't create multiple copies of the same function.
2. Prototype vs __proto__
This topic confused me at first, but here's the difference.
prototype
- Exists on functions.
- Used when creating new objects with the
newkeyword. - Stores shared methods.
Example:
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {};
const p1 = new Person("Harshith");
p1 will have a property called "proto" there it stores the prototype of Person
p1.proto=Person.prototype;
__proto__
- Exists on objects.
- Points to the object's prototype.
- JavaScript follows this link while searching for properties.
Example:
const person = new Person("Harshith");
console.log(person.proto === Person.prototype);
A simple way to remember:
-
prototypebelongs to functions. -
__proto__belongs to objects.
3. Prototype Chain
When JavaScript cannot find a property inside an object, it searches its prototype.
If it is still not found, it continues searching through the next prototype until it reaches null.
Example:
const animal = {
eat() {
console.log("Eating");
}
};
const dog = Object.create(animal);
dog.eat();
JavaScript searches like this:
dog
↓
animal.prototype
↓
null
This searching process is called the Prototype Chain.
4. Object.create()
Object.create() creates a new object whose prototype is another object.
Example:
const animal = {
eat() {
console.log("Eating");
}
};
const dog = Object.create(animal);
dog.eat();
The dog object doesn't have its own eat() method.
It inherits it from animal.
5. Classes are Syntactic Sugar
Before ES6, constructor functions were used to create objects.
function Person(name) {
this.name = name;
}
Person.prototype.greet=function (){
console.log(this.name);
}
Now we can write:
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log(this.name);
}
Enter fullscreen mode Exit fullscreen mode
}
Both work almost the same internally.
A class is simply a cleaner and easier syntax built on top of JavaScript's prototype system.
6. Inheritance
Inheritance allows one class to reuse the properties and methods of another class.
Example:
class Animal {
eat() {
console.log("Eating");
}
}
class Dog extends Animal {}
const dog = new Dog();
dog.eat();
Since Dog extends Animal, it inherits the eat() method.
This helps us avoid writing the same code again.
7. Polymorphism
Polymorphism means one method can behave differently depending on the object that calls it.
In JavaScript, this is usually achieved by overriding a parent class method in a child class.
Example
class Animal {
speak() {
console.log("Animal makes a sound");
}
}
class Dog extends Animal {
speak() {
console.log("Dog barks");
}
}
class Cat extends Animal {
speak() {
console.log("Cat meows");
}
}
const dog = new Dog();
const cat = new Cat();
dog.speak();
cat.speak();
Output:
Dog barks
Cat meows
Here, all classes have the same speak() method, but each class provides its own implementation. This is called polymorphism.
8. Abstraction
Abstraction means showing only the necessary functionality and hiding the internal implementation.
For example, when we start a car, we only press the start button. We don't need to know how the engine, fuel system, and battery work internally.
JavaScript allows us to achieve abstraction using classes and private methods or private fields.
Example
class Car {
start() {
this.#checkFuel();
console.log("Car Started");
}
#checkFuel() {
console.log("Checking Fuel...");
}
Enter fullscreen mode Exit fullscreen mode
}
const car = new Car();
car.start();
Output:
Checking Fuel...
Car Started
Enter fullscreen mode Exit fullscreen mode
The user only calls start(). The internal method #checkFuel() remains hidden from outside the class. This makes the code easier to use and protects the internal implementation.
9. Static Members
Normally, we create an object before calling a method.
const obj = new Person();
But some methods belong to the class itself, not to its objects.
These are called static methods.
Example:
class MathUtil {
static add(a, b) {
return a + b;
}
}
console.log(MathUtil.add(5, 3));
Notice that we didn't create an object.
10. Getters and Setters
Getters and setters allow us to control how properties are read and updated.
Example:
class Person {
constructor(name) {
this._name = name;
}
get name() {
return this._name;
}
set name(value) {
if (value.length >= 3) {
this._name = value;
}
}
Enter fullscreen mode Exit fullscreen mode
}
This helps us validate data before updating it.
11. Encapsulation Using Closures
Encapsulation means hiding data so it cannot be changed directly.
Example:
function BankAccount() {
let balance = 1000;
return {
deposit(amount) {
balance += amount;
},
getBalance() {
return balance;
}
};
Enter fullscreen mode Exit fullscreen mode
}
const account = BankAccount();
account.deposit(500);
console.log(account.getBalance());
The variable balance cannot be accessed directly from outside the function.
12. Private Fields (#field)
Modern JavaScript provides another way to hide data using private fields.
Example:
class BankAccount {
#balance = 1000;
deposit(amount) {
this.#balance += amount;
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount();
account.deposit(500);
console.log(account.getBalance());
Trying to access:
account.#balance;
will cause an error because private fields can only be accessed inside the class.
Conclusion
At first, JavaScript prototypes can feel confusing because there are terms like prototype, __proto__, constructor functions, and classes. But once I understood that objects inherit methods through prototypes, everything started making sense.
Now I know that JavaScript's OOP system is built on prototypes, and classes simply make the syntax cleaner and easier to read. Understanding these concepts gives a strong foundation for writing better JavaScript code.
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.