Understanding Object-Oriented Programming in JavaScript

Now we previously studied functions and importance of objects up till now But now we move on to writing Object-Oriented Code in JS.For bigger data we need a way to organize related data and behaviour together. This is where Object-Oriented Programming, or simply OOP, comes in. It is basically a way of structuring code around objects such that the data and the actions on that data stay together.
What is Object-Oriented Programming?
Object-Oriented Programming is a programming style where we model things as objects. Each object stores some data and also has actions it can perform. Instead of writing redundant code we make an object of it and use it's instances..
A simple way to think of it is through a blueprint analogy. Suppose a game engine wants to spawn birds in a world. Instead of manually defining every bird one by one the game first defines a blueprint of what a bird should contain like name, wingspan and speed. After that blueprint is defined the engine can spawn multiple birds based on it.
// This is NOT actual class syntax yet
// Just imagine it as a conceptual "blueprint"
// Bird Blueprint
// -------------------
// name
// wingSpan
// speed
// isFlying
// actions:
// fly()
// chirp()
// land()
// From this blueprint the program could create:
// Sparrow, Eagle, Parrot etc
// Each bird would share the same structure
// but would store different values
// Example birds (conceptually):
// Sparrow -> small wings, medium speed
// Eagle -> very large wings, high speed
// Parrot -> medium wings, talks alot 😅
// In programming the blueprint becomes a CLASS
// and the actual birds become OBJECTS
So in OOP we first define the blueprint using a class then create objects from it.
What is a Class in JavaScript?
A class in javascript is basically a template used to create objects. It defines what properties the object should contain and what methods it can use. Classes were introduced in ES6 and they made object based code much cleaner to write aswell.
// A simple Bird class
class Bird {
// constructor runs automatically
constructor(name, wingSpan, speed){
this.name = name
this.wingSpan = wingSpan
this.speed = speed
this.isFlying = false
}
// method: bird starts flying
fly(){
this.isFlying = true
return `\({this.name} started flying at \){this.speed} km/h`
}
// method: bird stops flying
land(){
this.isFlying = false
return `${this.name} landed safely`
}
// simple method just describing the bird
describe(){
return `\({this.name} has a wingspan of \){this.wingSpan} cm`
}
}
// Creating bird objects
const sparrow = new Bird("Sparrow", 25, 40)
const eagle = new Bird("Eagle", 220, 120)
console.log(sparrow.describe())
console.log(eagle.fly())
console.log(eagle.land())
// Edge case example
// what if we accidentally pass wrong values
const weirdBird = new Bird("GlitchBird", null, undefined)
console.log(weirdBird.describe())
// might print strange result because values were wrong
// real applications would validate this data aswell
Here Bird is the class and sparrow and eagle are objects created from it.
Creating Objects using Classes
Once a class is defined we create objects using the new keyword. Each object will follow the structure of the class but store its own data.
class Parrot {
constructor(name, vocabularySize){
this.name = name
this.vocabularySize = vocabularySize
this.isTalking = false
}
speak(word){
this.isTalking = true
return `\({this.name} says: \){word}`
}
learnWord(){
this.vocabularySize += 1
return `${this.name} learned a new word`
}
describe(){
return `\({this.name} knows \){this.vocabularySize} words`
}
}
// Creating multiple parrots from same class
const rio = new Parrot("Rio", 50)
const kiwi = new Parrot("Kiwi", 120)
console.log(rio.describe())
console.log(kiwi.describe())
console.log(rio.speak("Hello"))
console.log(kiwi.learnWord())
// Edge case
// what if someone gives negative vocabulary size
const brokenParrot = new Parrot("Buggy", -10)
console.log(brokenParrot.describe())
// obviously makes no sense but JS won't stop us
// validation would be needed in real programs
Even though both parrots follow the same blueprint their values are different.
Constructor Method
Inside every class we usually define a special method called the constructor. This method runs automatically whenever a new object is created. Its main job is initializing the properties of that object.
class Owl {
constructor(name, nightVision){
this.name = name
this.nightVision = nightVision
this.energy = 100
}
hunt(){
if(this.energy <= 0){
return `${this.name} is too tired to hunt`
}
this.energy -= 20
return `${this.name} hunted successfully`
}
rest(){
this.energy += 30
return `${this.name} regained energy`
}
status(){
return `\({this.name} energy level: \){this.energy}`
}
}
const shadow = new Owl("Shadow", true)
console.log(shadow.hunt())
console.log(shadow.hunt())
console.log(shadow.hunt())
console.log(shadow.status())
// Edge case testing
// if energy becomes negative
shadow.energy = -5
console.log(shadow.hunt())
// output might become weird depending on values
The constructor receives values and assigns them using this.
Methods inside a Class
Classes can also contain functions which are called methods. These define what actions an object can perform. Every object created from the class automatically gets those methods aswell.
class Falcon {
constructor(name, maxSpeed){
this.name = name
this.maxSpeed = maxSpeed
this.currentSpeed = 0
}
dive(){
this.currentSpeed = this.maxSpeed
return `\({this.name} dives at \){this.maxSpeed} km/h`
}
glide(){
this.currentSpeed = this.maxSpeed / 3
return `${this.name} is gliding peacefully`
}
stop(){
this.currentSpeed = 0
return `${this.name} stopped flying`
}
status(){
return `\({this.name} speed: \){this.currentSpeed}`
}
}
const blaze = new Falcon("Blaze", 320)
console.log(blaze.dive())
console.log(blaze.status())
console.log(blaze.glide())
console.log(blaze.stop())
// silly edge case
// setting speed manually from outside
blaze.currentSpeed = 9999
console.log(blaze.status())
// shows why controlling access matters
Basic Idea of Encapsulation
Encapsulation simply means that the data and the methods that operate on that data stay together. Instead of random parts of the program modifying data we control those changes through methods.
class BirdNest {
constructor(location, eggs){
this.location = location
this.eggs = eggs
}
addEgg(){
this.eggs += 1
return `Egg added. Total eggs: ${this.eggs}`
}
hatchEgg(){
if(this.eggs <= 0){
return "No eggs left to hatch"
}
this.eggs -= 1
return `A chick hatched! Remaining eggs: ${this.eggs}`
}
nestInfo(){
return `Nest at \({this.location} has \){this.eggs} eggs`
}
}
const oakNest = new BirdNest("Oak Tree", 3)
console.log(oakNest.nestInfo())
console.log(oakNest.addEgg())
console.log(oakNest.hatchEgg())
console.log(oakNest.hatchEgg())
console.log(oakNest.hatchEgg())
console.log(oakNest.hatchEgg())
// Edge case
// someone messing with data directly
oakNest.eggs = -50
console.log(oakNest.nestInfo())
// clearly wrong but JS allows it
Conclusion
Now this concludes our introduction to Object-Oriented Programming in javascript. We understood the basic idea behind OOP and how it models real world concepts using objects. We also learnt what classes are, how objects are created from them, how constructors initialize properties and how methods define behaviour inside a class. OOP helps organize code better and makes reuse easier when building bigger applications aswell.
Thank You!




