OOP is a programming paradigm centered around objects rather than functions. OOP is not a programming language or tool.
✏️ Four Pillars of OOP
1. Encapsulation
Encapsulation means bundling data (properties) and methods (functions) into a single unit (class/object) and restricting direct access to some of the object’s data.
class BankAccount {
#balance; // private field
constructor(owner, balance) {
this.owner = owner;
this.#balance = balance;
}
// Public method to access private data safely
getBalance() {
return `Balance: $${this.#balance}`;
}
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
console.log(`Deposited $${amount}`);
}
}
}
const account = new BankAccount("Alice", 500);
console.log(account.getBalance()); // ✅ Balance: $500
account.deposit(200); // ✅ Deposited $200
console.log(account.getBalance()); // ✅ Balance: $700
// console.log(account.#balance); ❌ Error (private field)✅ Encapsulation hides internal details and provides controlled access.
2. Abstraction
Abstraction means hiding complex implementation details and showing only the necessary features to the user.
class Car {
startEngine() {
console.log("Engine started...");
}
drive() {
this.startEngine(); // Abstracted internal step
console.log("Car is moving...");
}
}
const myCar = new Car();
myCar.drive();
// Output:
// Engine started...
// Car is moving...✅ The user only calls drive() without worrying about how startEngine() works internally.
3. Inheritance
Inheritance allows a class (child) to use properties and methods of another class (parent).
class Animal {
eat() {
console.log("Eating...");
}
}
class Dog extends Animal {
bark() {
console.log("Woof!");
}
}
const d = new Dog();
d.eat(); // from Animal
d.bark(); // from Dog✅ Dog inherits from Animal
4. Polymorphism
Polymorphism means one method behaves differently depending on the object that calls it (many forms).
class Bird {
fly() {
console.log("Bird is flying");
}
}
class Penguin extends Bird {
fly() {
console.log("Penguins can't fly");
}
}
const b = new Bird();
const p = new Penguin();
b.fly(); // Bird is flying
p.fly(); // Penguins can't fly✅ Same method → different result.
<div class="infographic-oop2">
<style>
.infographic-oop2 {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--deny: #c0392b;
--display-font: 'Fraunces', Georgia, serif;
--body-font: 'Inter', system-ui, sans-serif;
}
.infographic-oop2[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--deny: #e0685a;
}
.infographic-oop2 {
box-sizing: border-box;
max-width: 880px;
margin: 0 auto;
background: var(--paper);
color: var(--ink);
font-family: var(--body-font);
}
.infographic-oop2 *, .infographic-oop2 *::before, .infographic-oop2 *::after { box-sizing: border-box; }
.infographic-oop2 .oop-inner { padding: 0 clamp(1.25rem, 5vw, 3rem); }
.infographic-oop2 h1 {
font-family: var(--display-font);
font-weight: 600;
font-size: clamp(1.7rem, 5vw, 2.4rem);
margin: 2rem 0 0.35rem;
letter-spacing: -0.01em;
}
.infographic-oop2 .oop-sub {
color: var(--ink-soft);
font-size: 0.95rem;
margin: 0 0 2rem;
}
.infographic-oop2 .pillars {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1px;
background: var(--line);
border: 1px solid var(--line);
margin-bottom: 2.5rem;
}
.infographic-oop2 .pillar-card {
background: var(--paper);
padding: 1.4rem 1.2rem 1.6rem;
display: flex;
flex-direction: column;
align-items: center;
}
.infographic-oop2 .pillar-card h2 {
font-family: var(--display-font);
font-weight: 600;
font-size: 1.05rem;
margin: 0 0 0.9rem;
}
.infographic-oop2 .pillar-card svg { width: 100%; max-width: 240px; height: auto; overflow: visible; }
.infographic-oop2 .pillar-controls {
margin-top: 0.9rem;
display: flex;
gap: 0.5rem;
}
.infographic-oop2 .ctl-btn {
font: inherit;
font-size: 0.78rem;
font-weight: 500;
color: var(--accent);
background: var(--accent-soft);
border: 1px solid transparent;
padding: 0.4rem 0.85rem;
cursor: pointer;
border-radius: 3px;
transition: background 0.15s;
}
.infographic-oop2 .ctl-btn:hover { background: var(--accent); color: var(--paper); }
.infographic-oop2 .ctl-btn.is-active { background: var(--accent); color: var(--paper); }
@media (max-width: 600px) {
.infographic-oop2 .pillars { grid-template-columns: 1fr; }
}
/* svg part transitions */
/* abstraction: the interface stays simple; the hood hides the steps behind it */
.infographic-oop2 .hood-cover {
transform-box: fill-box;
transform-origin: center;
transition: transform 0.4s ease, opacity 0.4s ease;
}
.infographic-oop2 #absScene.is-open .hood-cover {
transform: translateY(-46px) rotate(-6deg);
opacity: 0;
}
.infographic-oop2 .engine-step {
transform-box: fill-box;
transform-origin: center;
}
.infographic-oop2 #engineSteps.is-running .engine-step {
animation: engineStepPulse 0.5s ease;
}
@keyframes engineStepPulse {
0% { fill: var(--paper); transform: scale(1); }
45% { fill: var(--accent); transform: scale(1.15); }
100% { fill: var(--paper); transform: scale(1); }
}
/* encapsulation: direct access is blocked, only the method gate gets through */
.infographic-oop2 .deny-mark { opacity: 0; transition: opacity 0.15s ease; }
.infographic-oop2 #encScene.anim-deny .deny-mark { opacity: 1; }
.infographic-oop2 #encScene.anim-deny .enc-wall {
stroke: var(--deny);
transition: stroke 0.15s ease;
animation: encShake 0.5s ease;
}
@keyframes encShake {
10%, 90% { transform: translateX(-2px); }
20%, 80% { transform: translateX(3px); }
30%, 50%, 70% { transform: translateX(-5px); }
40%, 60% { transform: translateX(5px); }
}
.infographic-oop2 .enc-core {
transform-box: fill-box;
transform-origin: center;
transition: transform 0.25s ease;
}
.infographic-oop2 #encScene.anim-grant .enc-core { transform: scale(1.07); }
.infographic-oop2 #encScene.anim-grant .enc-core rect { stroke: var(--accent); transition: stroke 0.2s ease; }
/* inheritance: a call climbs the chain only if the child has no method of its own */
.infographic-oop2 .lookup-token { opacity: 0; transform-box: fill-box; }
@keyframes tokenTravel {
0% { opacity: 0; transform: translateY(0); }
8% { opacity: 1; }
45%, 55% { transform: translateY(-31px); opacity: 1; }
92% { transform: translateY(0); opacity: 1; }
100% { opacity: 0; transform: translateY(0); }
}
.infographic-oop2 .lookup-token.is-traveling { animation: tokenTravel 1s ease; }
.infographic-oop2 .method-badge { transition: fill 0.15s ease; }
@keyframes badgePulse {
0% { fill: var(--ink-soft); }
50% { fill: var(--accent); }
100% { fill: var(--ink-soft); }
}
.infographic-oop2 .method-badge.is-hit { animation: badgePulse 0.5s ease; }
.infographic-oop2 #lookupResult { opacity: 0; transition: opacity 0.2s ease; }
.infographic-oop2 #lookupResult.is-shown { opacity: 1; }
.infographic-oop2 .animal-group { opacity: 0; transition: opacity 0.3s ease; }
.infographic-oop2 .animal-group.is-shown { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
.infographic-oop2 *, .infographic-oop2 *::before, .infographic-oop2 *::after {
transition-duration: 0.001ms !important;
}
}
</style>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Fraunces:wght@600&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<div class="oop-inner">
<div class="pillars">
<!-- Encapsulation -->
<div class="pillar-card">
<h2>Encapsulation</h2>
<svg viewBox="0 0 240 170" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<g id="encScene">
<rect class="enc-wall" x="20" y="25" width="200" height="120" rx="14" fill="var(--paper-alt)" stroke="var(--accent)" stroke-width="2"/>
<text x="32" y="41" font-size="10" fill="var(--ink-soft)">Car object</text>
<g class="enc-core">
<rect x="80" y="68" width="80" height="50" rx="8" fill="var(--paper)" stroke="var(--line)" stroke-width="2"/>
<g transform="translate(150,64)">
<path d="M -6 4 A 6 6 0 0 1 6 4" fill="none" stroke="var(--ink-soft)" stroke-width="3" stroke-linecap="round"/>
<rect x="-9" y="3" width="18" height="13" rx="2" fill="var(--ink-soft)"/>
</g>
<text x="120" y="90" font-size="9" text-anchor="middle" fill="var(--ink-soft)">private speed</text>
<text id="speedValue" x="120" y="109" font-size="16" text-anchor="middle" fill="var(--accent)" font-weight="600">0</text>
</g>
<g class="deny-mark" transform="translate(37,93)">
<circle r="13" fill="none" stroke="var(--deny)" stroke-width="3"/>
<line x1="-8" y1="-8" x2="8" y2="8" stroke="var(--deny)" stroke-width="3"/>
</g>
<g transform="translate(120,138)">
<rect x="-42" y="-10" width="84" height="20" rx="10" fill="var(--accent-soft)" stroke="var(--accent)" stroke-width="1.5"/>
<text x="0" y="4" font-size="9" text-anchor="middle" fill="var(--accent)">accelerate()</text>
</g>
</g>
</svg>
<div class="pillar-controls">
<button class="ctl-btn" type="button" id="encDenyBtn">car.speed = 999</button>
<button class="ctl-btn" type="button" id="encGrantBtn">car.accelerate(10)</button>
</div>
</div>
<!-- Abstraction -->
<div class="pillar-card">
<h2>Abstraction</h2>
<svg viewBox="0 0 240 170" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<g class="abs-scene" id="absScene">
<rect x="45" y="15" width="150" height="42" rx="10" fill="var(--paper)" stroke="var(--line)" stroke-width="2"/>
<circle cx="75" cy="36" r="13" fill="var(--accent)"/>
<path d="M 71 30 L 71 42 L 82 36 Z" fill="var(--paper)"/>
<text x="98" y="33" font-size="9" fill="var(--ink-soft)">start()</text>
<text id="engineStatus" x="98" y="47" font-size="11" font-weight="600" fill="var(--accent)">Ready</text>
<g id="engineSteps">
<circle class="engine-step" cx="65" cy="105" r="15" fill="var(--paper)" stroke="var(--line)" stroke-width="2" style="animation-delay:0s"/>
<circle class="engine-step" cx="102" cy="105" r="15" fill="var(--paper)" stroke="var(--line)" stroke-width="2" style="animation-delay:0.12s"/>
<circle class="engine-step" cx="139" cy="105" r="15" fill="var(--paper)" stroke="var(--line)" stroke-width="2" style="animation-delay:0.24s"/>
<circle class="engine-step" cx="176" cy="105" r="15" fill="var(--paper)" stroke="var(--line)" stroke-width="2" style="animation-delay:0.36s"/>
<text x="65" y="108" font-size="7" text-anchor="middle" fill="var(--ink-soft)">fuel</text>
<text x="102" y="108" font-size="7" text-anchor="middle" fill="var(--ink-soft)">spark</text>
<text x="139" y="108" font-size="7" text-anchor="middle" fill="var(--ink-soft)">burn</text>
<text x="176" y="108" font-size="7" text-anchor="middle" fill="var(--ink-soft)">turn</text>
</g>
<g class="hood-cover">
<rect x="45" y="72" width="150" height="66" rx="10" fill="var(--paper-alt)" stroke="var(--line)" stroke-width="2"/>
<text x="120" y="109" font-size="9" text-anchor="middle" fill="var(--ink-soft)">hidden steps</text>
</g>
</g>
</svg>
<div class="pillar-controls">
<button class="ctl-btn" type="button" id="absStartBtn">start()</button>
<button class="ctl-btn" type="button" data-toggle="absScene" data-labels="Lift hood,Close hood">Lift hood</button>
</div>
</div>
<!-- Inheritance -->
<div class="pillar-card">
<h2>Inheritance</h2>
<svg viewBox="0 0 240 170" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<line x1="120" y1="57" x2="120" y2="80" stroke="var(--line)" stroke-width="2"/>
<rect x="60" y="15" width="120" height="42" rx="8" fill="var(--accent-soft)" stroke="var(--accent)" stroke-width="2"/>
<text x="120" y="32" font-size="12" font-weight="600" text-anchor="middle" fill="var(--ink)">Vehicle</text>
<text id="driveBadge" class="method-badge" x="120" y="48" font-size="9" text-anchor="middle" fill="var(--ink-soft)">drive()</text>
<rect x="60" y="80" width="120" height="54" rx="8" fill="var(--paper)" stroke="var(--line)" stroke-width="2"/>
<text x="120" y="98" font-size="12" font-weight="600" text-anchor="middle" fill="var(--ink)">Car</text>
<text id="honkBadge" class="method-badge" x="120" y="114" font-size="9" text-anchor="middle" fill="var(--ink-soft)">honk()</text>
<circle id="lookupToken" class="lookup-token" cx="120" cy="80" r="6" fill="var(--accent)"/>
<text id="lookupResult" x="120" y="152" font-size="10" text-anchor="middle" fill="var(--accent)" font-weight="600"></text>
</svg>
<div class="pillar-controls">
<button class="ctl-btn" type="button" id="driveBtn">car.drive()</button>
<button class="ctl-btn" type="button" id="honkBtn">car.honk()</button>
</div>
</div>
<!-- Polymorphism -->
<div class="pillar-card">
<h2>Polymorphism</h2>
<svg viewBox="0 0 240 170" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<g class="animal-group" id="animalCat">
<path d="M 95 60 L 105 40 L 112 62 Z" fill="var(--accent-soft)" stroke="var(--accent)" stroke-width="2"/>
<path d="M 128 62 L 135 40 L 145 60 Z" fill="var(--accent-soft)" stroke="var(--accent)" stroke-width="2"/>
<ellipse cx="120" cy="90" rx="34" ry="28" fill="var(--accent-soft)" stroke="var(--accent)" stroke-width="2"/>
<circle cx="110" cy="86" r="2.5" fill="var(--ink)"/>
<circle cx="130" cy="86" r="2.5" fill="var(--ink)"/>
<path d="M 40 55 Q 90 40 108 62" fill="none" stroke="var(--line)" stroke-width="2"/>
<rect x="30" y="30" width="66" height="26" rx="6" fill="var(--paper)" stroke="var(--line)" stroke-width="2"/>
<text x="63" y="47" font-size="11" text-anchor="middle" fill="var(--ink)">Meow!</text>
<text x="120" y="140" font-size="11" text-anchor="middle" fill="var(--ink-soft)">Cat</text>
</g>
<g class="animal-group" id="animalDog">
<ellipse cx="88" cy="80" rx="12" ry="20" fill="var(--accent-soft)" stroke="var(--accent)" stroke-width="2"/>
<ellipse cx="120" cy="90" rx="34" ry="26" fill="var(--accent-soft)" stroke="var(--accent)" stroke-width="2"/>
<circle cx="110" cy="86" r="2.5" fill="var(--ink)"/>
<circle cx="132" cy="86" r="2.5" fill="var(--ink)"/>
<path d="M 40 55 Q 90 40 108 62" fill="none" stroke="var(--line)" stroke-width="2"/>
<rect x="30" y="30" width="66" height="26" rx="6" fill="var(--paper)" stroke="var(--line)" stroke-width="2"/>
<text x="63" y="47" font-size="11" text-anchor="middle" fill="var(--ink)">Woof!</text>
<text x="120" y="140" font-size="11" text-anchor="middle" fill="var(--ink-soft)">Dog</text>
</g>
<g class="animal-group" id="animalDuck">
<path d="M 148 82 L 168 78 L 148 92 Z" fill="var(--accent)"/>
<ellipse cx="118" cy="92" rx="32" ry="24" fill="var(--accent-soft)" stroke="var(--accent)" stroke-width="2"/>
<circle cx="128" cy="82" r="2.5" fill="var(--ink)"/>
<path d="M 40 55 Q 90 40 106 66" fill="none" stroke="var(--line)" stroke-width="2"/>
<rect x="30" y="30" width="70" height="26" rx="6" fill="var(--paper)" stroke="var(--line)" stroke-width="2"/>
<text x="65" y="47" font-size="11" text-anchor="middle" fill="var(--ink)">Quack!</text>
<text x="120" y="140" font-size="11" text-anchor="middle" fill="var(--ink-soft)">Duck</text>
</g>
</svg>
<div class="pillar-controls">
<button class="ctl-btn" type="button" id="speakBtn">Call .speak()</button>
</div>
</div>
</div>
</div>
<script>
(function () {
var wrapper = document.currentScript.closest('.infographic-oop2')
|| document.querySelector('.infographic-oop2');
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
var match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
/* encapsulation: direct write is blocked, method call gets through */
var encScene = wrapper.querySelector('#encScene');
var speedValueEl = wrapper.querySelector('#speedValue');
var speed = 0;
wrapper.querySelector('#encDenyBtn').addEventListener('click', function () {
encScene.classList.remove('anim-deny');
void encScene.offsetWidth;
encScene.classList.add('anim-deny');
setTimeout(function () { encScene.classList.remove('anim-deny'); }, 650);
});
wrapper.querySelector('#encGrantBtn').addEventListener('click', function () {
speed += 10;
speedValueEl.textContent = speed;
encScene.classList.remove('anim-grant');
void encScene.offsetWidth;
encScene.classList.add('anim-grant');
setTimeout(function () { encScene.classList.remove('anim-grant'); }, 350);
});
/* abstraction: pressing start() always runs the same steps, whether or not you can see them */
var engineStatus = wrapper.querySelector('#engineStatus');
var engineSteps = wrapper.querySelector('#engineSteps');
var runTimer = null;
wrapper.querySelector('#absStartBtn').addEventListener('click', function () {
engineStatus.textContent = 'Running';
engineSteps.classList.remove('is-running');
void engineSteps.offsetWidth;
engineSteps.classList.add('is-running');
clearTimeout(runTimer);
runTimer = setTimeout(function () { engineStatus.textContent = 'Ready'; }, 900);
});
/* generic open/close toggles (abstraction hood) */
wrapper.querySelectorAll('.ctl-btn[data-toggle]').forEach(function (btn) {
var targetId = btn.getAttribute('data-toggle');
var labels = btn.getAttribute('data-labels').split(',');
var target = wrapper.querySelector('#' + targetId);
btn.addEventListener('click', function () {
var open = target.classList.toggle('is-open');
btn.textContent = open ? labels[1] : labels[0];
btn.classList.toggle('is-active', open);
});
});
/* inheritance: drive() isn't on Car, so the call climbs to Vehicle; honk() is Car's own, so it resolves locally */
var lookupToken = wrapper.querySelector('#lookupToken');
var driveBadge = wrapper.querySelector('#driveBadge');
var honkBadge = wrapper.querySelector('#honkBadge');
var lookupResult = wrapper.querySelector('#lookupResult');
var resultTimer = null;
function showResult(text) {
clearTimeout(resultTimer);
lookupResult.textContent = text;
lookupResult.classList.add('is-shown');
resultTimer = setTimeout(function () { lookupResult.classList.remove('is-shown'); }, 1100);
}
function pulseBadge(el) {
el.classList.remove('is-hit');
void el.offsetWidth;
el.classList.add('is-hit');
}
wrapper.querySelector('#driveBtn').addEventListener('click', function () {
lookupToken.classList.remove('is-traveling');
void lookupToken.offsetWidth;
lookupToken.classList.add('is-traveling');
setTimeout(function () { pulseBadge(driveBadge); }, 430);
showResult('found in Vehicle');
});
wrapper.querySelector('#honkBtn').addEventListener('click', function () {
pulseBadge(honkBadge);
showResult("Car's own method");
});
/* polymorphism: cycle animals */
var animals = ['animalCat', 'animalDog', 'animalDuck'];
var animalEls = animals.map(function (id) { return wrapper.querySelector('#' + id); });
var idx = 0;
function showAnimal(i) {
animalEls.forEach(function (el, j) { el.classList.toggle('is-shown', j === i); });
}
showAnimal(0);
wrapper.querySelector('#speakBtn').addEventListener('click', function () {
idx = (idx + 1) % animals.length;
showAnimal(idx);
});
})();
</script>
</div>📌 Quick Summary
Encapsulation → Protects data (
private fields, controlled methods).Abstraction → Shows essential info, hides details (e.g.,
drive()hidesstartEngine()).Inheritance → Reuse properties/methods from parent class (
extends).Polymorphism → Same method works differently in different classes.