Yeh guide bilkul step-by-step hai — inner working aur process ke saath samjhenge ke OOP JavaScript mein kaise kaam karta hai.
- OOP Fundamentals
- Objects Kaise Bantay Hain
- Prototype System - Inner Working
- Constructor Function - Step by Step
- ES6 Classes - Andar Kya Hota Hai
- Inheritance - Process Explained
- Encapsulation - Data Hiding
- Polymorphism - Same Name, Different Behavior
- this Keyword - Inner Working
- Memory Management
- Common Mistakes
English: Object-Oriented Programming means organizing code using objects that contain data (properties) and actions (methods).
Roman Urdu: OOP ka matlab hai ke hum apne code ko objects ki form mein organize karte hain. Objects mein data (properties) aur functions (methods) hotay hain.
// Real world car ko code mein represent karna
let car = {
// Properties (data)
brand: "Toyota",
color: "Red",
speed: 0,
// Methods (actions)
start() {
console.log(this.brand + " start ho gayi");
},
accelerate() {
this.speed += 10;
console.log("Speed: " + this.speed + " km/h");
}
};
car.start(); // "Toyota start ho gayi"
car.accelerate(); // "Speed: 10 km/h"Process samjhein:
carek object haibrand,color,speedproperties hain (data store karte hain)start(),accelerate()methods hain (actions perform karte hain)thiskeyword current object ko point karta hai
| Principle | English | Roman Urdu | Example |
|---|---|---|---|
| Abstraction | Hide complexity, show essentials | Complexity chhupakar sirf zaruri cheezein dikhana | ATM machine: button press karo, andar ka mechanism nahi dekhna |
| Encapsulation | Bundle data with methods that use it | Data aur methods ko ek saath rakhna aur control karna | Private variables jo bahar se access nahi ho sakte |
| Inheritance | Child class gets parent's features | Ek class dusri class ki properties inherit kare | Dog class Animal se inherit kare |
| Polymorphism | Same method, different implementation | Ek naam, alag-alag kaam | speak() Dog mein bark, Cat mein meow |
JavaScript mein objects banane ke 5 tareeqay hain. Har ek ka inner working samjhte hain.
// Sabse simple tareeqa
let student = {
name: "Zohaib",
age: 21,
study: function() {
console.log(this.name + " parh raha hai");
}
};
student.study(); // "Zohaib parh raha hai"Inner Working:
- JavaScript engine memory mein ek object create karta hai
name,ageproperties us object mein store hoti hainstudymethod bhi us object ka part ban jata hai- Har property aur method direct object mein stored hai
Limitation: Agar 100 students banana ho to 100 baar likhna parega!
function Student(name, age) {
this.name = name; // property set kar rahe hain
this.age = age;
}
// Prototype pe method add kar rahe hain (shared method)
Student.prototype.study = function() {
console.log(this.name + " parh raha hai");
};
let s1 = new Student("Ali", 20);
let s2 = new Student("Sara", 19);
s1.study(); // "Ali parh raha hai"
s2.study(); // "Sara parh raha hai"Inner Working jab new Student("Ali", 20) chalata hai:
Step 1: Empty object create hota hai
let obj = {}; // internallyStep 2: Object ki [[Prototype]] set hoti hai
obj.__proto__ = Student.prototype;Step 3: Constructor function call hota hai this = obj ke saath
// this = obj
this.name = "Ali"; // obj.name = "Ali"
this.age = 20; // obj.age = 20Step 4: Object return hota hai
return obj; // s1 = objMemory Diagram:
s1 ──┐
├─→ {name: "Ali", age: 20}
│ __proto__ ──→ Student.prototype ──→ {study: function}
s2 ──┘
└─→ {name: "Sara", age: 19}
__proto__ ──→ Student.prototype (SAME!)
class Student {
constructor(name, age) {
this.name = name;
this.age = age;
}
study() {
console.log(this.name + " parh raha hai");
}
}
let s1 = new Student("Hamza", 22);
s1.study(); // "Hamza parh raha hai"Inner Working:
classkeyword sirf syntactic sugar hai- Internally wahi constructor function + prototype system use hota hai
study()methodStudent.prototypepe jata hai
Proof:
console.log(typeof Student); // "function"
console.log(s1.__proto__ === Student.prototype); // trueClass vs Constructor - Dono Same:
// Ye dono bilkul same kaam karte hain internally:
// Constructor function
function Student(name) { this.name = name; }
Student.prototype.study = function() { /*...*/ };
// ES6 Class
class Student {
constructor(name) { this.name = name; }
study() { /*...*/ }
}const studentMethods = {
study() {
console.log(this.name + " parh raha hai");
}
};
let s1 = Object.create(studentMethods);
s1.name = "Ahmed";
s1.study(); // "Ahmed parh raha hai"Inner Working:
Object.create(proto)ek naya object banata hai- Us object ki
[[Prototype]]protoset kar deta hai - Direct prototype link set hota hai
function createStudent(name, age) {
return {
name: name,
age: age,
study() {
console.log(this.name + " parh raha hai");
}
};
}
let s1 = createStudent("Usman", 21);
s1.study(); // "Usman parh raha hai"Inner Working:
- Simple function hai jo object return karta hai
newkeyword ki zarurat nahi- Har call pe naya object banta hai
JavaScript mein classical inheritance nahi hai — yeh prototype-based hai.
const animal = {
eats: true,
walk() {
console.log("Animal chal raha hai");
}
};
const dog = Object.create(animal);
dog.barks = true;
console.log(dog.barks); // true (dog mein hai)
console.log(dog.eats); // true (animal se mila)
dog.walk(); // "Animal chal raha hai" (animal se)Step-by-Step Process jab dog.walk() call hota hai:
Step 1: JavaScript dog object mein walk dhoondhta hai
dog.walk → undefined (nahi mila)Step 2: dog ki [[Prototype]] mein dhoondhta hai (yaani animal)
dog.__proto__.walk → function found! ✓Step 3: Method ko call karta hai this = dog ke saath
walk.call(dog) // "Animal chal raha hai"dog object
│
├─ barks: true
├─ [[Prototype]] ──→ animal object
│
├─ eats: true
├─ walk: function
├─ [[Prototype]] ──→ Object.prototype
│
├─ toString: function
├─ hasOwnProperty: function
├─ [[Prototype]] ──→ null
Lookup Process:
- Property pehle object mein dhoondhte hain
- Phir
__proto__(prototype) mein - Phir
__proto__ke__proto__mein - Chain ke end tak (null)
- Agar mil gayi to return, warna
undefined
let str = "hello";
console.log(str.toUpperCase()); // "HELLO"Inner Working:
"hello" (primitive)
↓
JavaScript temporarily converts to String object
↓
String {"hello"}
├─ [[Prototype]] → String.prototype
├─ toUpperCase: function ✓
├─ toLowerCase: function
├─ split: function
└─ [[Prototype]] → Object.prototype
Process:
"hello"primitive hai- JavaScript temporarily
new String("hello")banata hai String.prototype.toUpperCasemethod call hota hai- Result milne ke baad temporary object delete ho jata hai
Chalo ab poora process detail mein samjhte hain:
function Car(brand, color) {
this.brand = brand;
this.color = color;
console.log("Constructor chal raha hai, this =", this);
}
Car.prototype.start = function() {
console.log(this.brand + " start ho gayi");
};
Car.prototype.info = function() {
console.log(`${this.brand} car ${this.color} rang ki hai`);
};JavaScript Engine ke andar kya hota hai:
// Internally
let newObj = {};newObj.__proto__ = Car.prototype;
// Ab newObj prototype chain mein hai:
// newObj → Car.prototype → Object.prototype → null// Function call hota hai with this = newObj
Car.call(newObj, "Honda", "Blue");
// Andar:
this.brand = "Honda"; // newObj.brand = "Honda"
this.color = "Blue"; // newObj.color = "Blue"
console.log("Constructor chal raha hai, this =", newObj);// Agar constructor explicitly object return nahi karta:
return newObj;
// myCar ab newObj ko point karta hai
let myCar = newObj;myCar
│
└──→ Object
├── brand: "Honda"
├── color: "Blue"
└── [[Prototype]]: Car.prototype
├── start: function
├── info: function
└── [[Prototype]]: Object.prototype
Step 1: myCar mein start dhoondhte hain
myCar.start → undefined (not found)Step 2: myCar.__proto__ (yaani Car.prototype) mein dhoondhte hain
myCar.__proto__.start → function found! ✓Step 3: Function execute hota hai with this = myCar
start.call(myCar);
// Andar: console.log(this.brand + " start ho gayi")
// Output: "Honda start ho gayi"let car1 = new Car("Toyota", "Red");
let car2 = new Car("Suzuki", "White");
car1.start(); // "Toyota start ho gayi"
car2.start(); // "Suzuki start ho gayi"Memory mein kya hota hai:
car1 ──→ {brand: "Toyota", color: "Red"}
│
└──→ [[Prototype]]: Car.prototype ←──┐
├── start │
└── info │ (SHARED)
│
car2 ──→ {brand: "Suzuki", color: "White"} │
│ │
└──→ [[Prototype]]: Car.prototype ───┘
Key Point:
- Har car ki apni
brandaurcolorhai (unique data) - Lekin
startaurinfomethods shared hain (ek hi copy) - Yeh memory efficient hai!
class Animal {
constructor(name) {
this.name = name;
console.log("Animal constructor called");
}
speak() {
console.log(this.name + " awaz kar raha hai");
}
sleep() {
console.log(this.name + " so raha hai");
}
}
let cat = new Animal("Billi");
cat.speak(); // "Billi awaz kar raha hai"Class ko internally ye convert hota hai:
// Internally
function Animal(name) {
this.name = name;
console.log("Animal constructor called");
}
Animal.prototype.speak = function() {
console.log(this.name + " awaz kar raha hai");
};
Animal.prototype.sleep = function() {
console.log(this.name + " so raha hai");
};Proof karo:
console.log(typeof Animal); // "function"
console.log(cat.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.speak); // function| Feature | Constructor Function | ES6 Class |
|---|---|---|
| Syntax | function Person() {} |
class Person {} |
| Methods | Person.prototype.method = ... |
method() {} inside class |
| Constructor | Function itself | constructor() {} method |
new required |
Optional (but should use) | Mandatory (error without it) |
| Hoisting | Function hoisting | No hoisting |
| Strict mode | Optional | Automatic |
class Animal {
constructor(name) {
this.name = name;
}
eat() {
console.log(this.name + " kha raha hai");
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // Parent constructor call
this.breed = breed;
}
bark() {
console.log(this.name + " bhonk raha hai: Woof!");
}
}
let myDog = new Dog("Tommy", "German Shepherd");
myDog.eat(); // "Tommy kha raha hai" (Animal se)
myDog.bark(); // "Tommy bhonk raha hai: Woof!" (Dog ka)let newObj = {}; // internally// Dog.prototype ki [[Prototype]] Animal.prototype ko point karti hai
Dog.prototype.__proto__ = Animal.prototype;
// newObj ki [[Prototype]] Dog.prototype ko point karti hai
newObj.__proto__ = Dog.prototype;Prototype Chain ban gayi:
newObj → Dog.prototype → Animal.prototype → Object.prototype → null
3a. Dog constructor start:
// this = newObj (but abhi incomplete hai)
constructor(name, breed) {
super(name); // ← Yeh line parent ko call karegi
this.breed = breed;
}3b. super(name) calls Animal constructor:
// Animal.call(newObj, "Tommy")
Animal.constructor.call(newObj, "Tommy");
// Andar:
this.name = "Tommy"; // newObj.name = "Tommy"3c. Dog constructor continue:
this.breed = "German Shepherd"; // newObj.breed = "German Shepherd"return newObj; // myDog = newObjmyDog
│
└──→ Object
├── name: "Tommy"
├── breed: "German Shepherd"
└── [[Prototype]]: Dog.prototype
├── bark: function
└── [[Prototype]]: Animal.prototype
├── eat: function
└── [[Prototype]]: Object.prototype
Process:
myDogmeineatdhoondhte hain → ❌ Nahi milamyDog.__proto__(Dog.prototype) mein → ❌ Nahi milaDog.prototype.__proto__(Animal.prototype) mein → ✅ Mil gaya!eat.call(myDog)execute hota hai- Output: "Tommy kha raha hai"
class Animal {
speak() {
console.log("Animal awaz kar raha hai");
}
}
class Cat extends Animal {
speak() {
console.log("Meow meow!");
}
}
class Dog extends Animal {
speak() {
console.log("Woof woof!");
}
}
let cat = new Cat();
let dog = new Dog();
cat.speak(); // "Meow meow!"
dog.speak(); // "Woof woof!"Inner Working:
- Jab
cat.speak()call hota hai - Pehle
catmein dhoondhte hain → Nahi - Phir
Cat.prototypemein → Mil gaya! ✓ - Animal.prototype tak jaane ki zarurat nahi
- Nearest method use hota hai (overriding)
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return this.name + " awaz kar raha hai";
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // Parent constructor call
this.breed = breed;
}
speak() {
let parentMsg = super.speak(); // Parent method call
return parentMsg + " - Woof!";
}
}
let dog = new Dog("Bruno", "Bulldog");
console.log(dog.speak());
// "Bruno awaz kar raha hai - Woof!"super ke 2 uses:
- Constructor mein:
super(args)→ Parent constructor call karta hai - Method mein:
super.methodName()→ Parent ka method call karta hai
Process:
super.speak()
↓
Animal.prototype.speak.call(this)
↓
"Bruno awaz kar raha hai"Encapsulation ka matlab: Data ko hide karna taake wo directly access na ho sake.
class BankAccount {
#balance = 0; // Private field
#accountNumber; // Private field
constructor(accNum, initialBalance) {
this.#accountNumber = accNum;
this.#balance = initialBalance;
}
// Public method to access private data
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
console.log(`${amount} rupees jama hogaye`);
}
}
withdraw(amount) {
if (amount > 0 && amount <= this.#balance) {
this.#balance -= amount;
console.log(`${amount} rupees nikale`);
} else {
console.log("Insufficient balance");
}
}
getBalance() {
return this.#balance;
}
}
let account = new BankAccount("ACC001", 5000);
account.deposit(2000); // "2000 rupees jama hogaye"
console.log(account.getBalance()); // 7000
account.withdraw(3000); // "3000 rupees nikale"
// console.log(account.#balance); // ❌ SyntaxError - private hai!Inner Working:
#balanceaur#accountNumbertruly private hain- JavaScript engine compile time pe check karta hai
- Bahar se access karne pe syntax error aata hai
- Sirf class ke andar access ho sakti hain
function createBankAccount(initialBalance) {
// Private variable (closure mein hai)
let balance = initialBalance;
// Public methods return kar rahe hain
return {
deposit(amount) {
if (amount > 0) {
balance += amount;
console.log(`${amount} jama hogaye. Balance: ${balance}`);
}
},
withdraw(amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
console.log(`${amount} nikale. Balance: ${balance}`);
} else {
console.log("Insufficient balance");
}
},
getBalance() {
return balance;
}
};
}
let acc = createBankAccount(1000);
acc.deposit(500); // "500 jama hogaye. Balance: 1500"
console.log(acc.getBalance()); // 1500
// console.log(acc.balance); // undefined - access nahi hai!Inner Working (Closure Magic):
createBankAccount() call hota hai
↓
Execution context banta hai with local variable `balance`
↓
Object return hota hai with 3 methods
↓
Methods closure banate hain - wo `balance` ko access kar sakte hain
↓
Function khatam hone ke baad bhi `balance` memory mein rahta hai
(kyunki methods us ko reference kar rahe hain)
↓
Bahar se `balance` directly access nahi ho sakta
Diagram:
acc ──→ {
deposit: function (closure → balance),
withdraw: function (closure → balance),
getBalance: function (closure → balance)
}
Scope Chain:
Methods → Closure (balance: 1000) → Global Scope
const privateData = new WeakMap();
class User {
constructor(name, password) {
this.name = name; // Public
// Private data WeakMap mein store
privateData.set(this, {
password: password
});
}
checkPassword(input) {
const data = privateData.get(this);
return data.password === input;
}
}
let user = new User("Zohaib", "secret123");
console.log(user.name); // "Zohaib" - public
console.log(user.checkPassword("wrong")); // false
console.log(user.checkPassword("secret123")); // true
// console.log(user.password); // undefined - private!class Counter {
#count = 0;
#maxCount = 100;
increment() {
if (this.#count < this.#maxCount) {
this.#count++;
} else {
console.log("Maximum limit reached");
}
}
decrement() {
if (this.#count > 0) {
this.#count--;
}
}
getValue() {
return this.#count;
}
}
let counter = new Counter();
counter.increment();
counter.increment();
console.log(counter.getValue()); // 2
// Agar encapsulation na hota:
// counter.#count = -50; // Invalid state!
// counter.#count = 1000; // Limit cross!Faida:
- Data protection: Invalid values set nahi ho sakte
- Controlled access: Sirf methods ke through access
- Easy to change: Internal implementation change kar sakte ho without breaking code
- Validation: Har change pe validation add kar sakte ho
Polymorphism ka matlab: Ek naam, alag-alag kaam
Same method name, different implementations in different classes.
class Shape {
area() {
return 0; // Default
}
describe() {
console.log("Main ek shape hoon");
}
}
class Circle extends Shape {
constructor(radius) {
super();
this.radius = radius;
}
area() {
return Math.PI * this.radius ** 2;
}
describe() {
console.log(`Main ${this.radius} radius ka circle hoon`);
}
}
class Rectangle extends Shape {
constructor(width, height) {
super();
this.width = width;
this.height = height;
}
area() {
return this.width * this.height;
}
describe() {
console.log(`Main ${this.width}x${this.height} ka rectangle hoon`);
}
}
// Polymorphism in action
let shapes = [
new Circle(5),
new Rectangle(4, 6),
new Circle(3)
];
shapes.forEach(shape => {
shape.describe();
console.log("Area:", shape.area());
console.log("---");
});Output:
Main 5 radius ka circle hoon
Area: 78.53981633974483
---
Main 4x6 ka rectangle hoon
Area: 24
---
Main 3 radius ka circle hoon
Area: 28.274333882308138
---
Inner Working:
- Loop mein har
shapealag object hai - Jab
shape.area()call hota hai, JavaScript check karta hai actual object kya hai - Agar
Circlehai toCircle.prototype.areacall hota hai - Agar
Rectanglehai toRectangle.prototype.areacall hota hai - Runtime pe decide hota hai kon sa method call hoga
class Payment {
constructor(amount) {
this.amount = amount;
}
processPayment() {
console.log("Processing payment...");
}
}
class CreditCardPayment extends Payment {
constructor(amount, cardNumber) {
super(amount);
this.cardNumber = cardNumber;
}
processPayment() {
console.log(`Processing credit card payment of Rs.${this.amount}`);
console.log(`Card: ****${this.cardNumber.slice(-4)}`);
}
}
class EasypaisaPayment extends Payment {
constructor(amount, phoneNumber) {
super(amount);
this.phoneNumber = phoneNumber;
}
processPayment() {
console.log(`Processing Easypaisa payment of Rs.${this.amount}`);
console.log(`Phone: ${this.phoneNumber}`);
}
}
class BankTransfer extends Payment {
constructor(amount, accountNumber) {
super(amount);
this.accountNumber = accountNumber;
}
processPayment() {
console.log(`Processing bank transfer of Rs.${this.amount}`);
console.log(`Account: ${this.accountNumber}`);
}
}
// Polymorphism ka faida
function checkout(payment) {
payment.processPayment(); // Kon sa method call hoga? Runtime pe decide hoga!
}
let payment1 = new CreditCardPayment(5000, "1234567890123456");
let payment2 = new EasypaisaPayment(3000, "03001234567");
let payment3 = new BankTransfer(10000, "PK12BANK0123456789");
checkout(payment1);
console.log("---");
checkout(payment2);
console.log("---");
checkout(payment3);Faida:
checkout()function ko pata nahi kon sa payment type hai- Har payment type apna khud ka
processPayment()implementation use karta hai - Naye payment methods add karna easy hai
- Code flexible aur maintainable ban jata hai
this JavaScript mein sabse tricky concept hai. Isko samajhna bohot zaroori hai!
function showThis() {
console.log(this);
}
showThis(); // undefined (strict mode) ya Window (non-strict)Inner Working:
- Simple function call mein
thisundefined hota hai (strict mode) - Non-strict mode mein global object (
windowbrowser mein)
const person = {
name: "Zohaib",
greet() {
console.log("Hello, I'm " + this.name);
}
};
person.greet(); // "Hello, I'm Zohaib"Inner Working:
person.greet()
↓
greet.call(person) // this = person
↓
console.log("Hello, I'm " + person.name)Rule: Jis object ne method call kiya, this usi ko point karega.
const person = {
name: "Ali",
greet() {
console.log("Hello, I'm " + this.name);
}
};
person.greet(); // ✅ "Hello, I'm Ali"
const greetFunction = person.greet;
greetFunction(); // ❌ "Hello, I'm undefined"Kya hua?
Step 1: Method assign karte waqt
const greetFunction = person.greet;
// Ab greetFunction ek simple function hai, object se link khatamStep 2: Simple function call
greetFunction(); // this = undefined (strict) ya windowSolutions:
const person = {
name: "Sara",
greet: () => {
console.log("Hello, I'm " + this.name);
}
};
// ⚠️ Arrow function apne parent scope ki `this` use karta hai
// Object method ke liye use mat karo!const person = {
name: "Hamza",
greet() {
console.log("Hello, I'm " + this.name);
}
};
const boundGreet = person.greet.bind(person);
boundGreet(); // ✅ "Hello, I'm Hamza"Inner Working of bind():
person.greet.bind(person)
↓
// Naya function return karta hai
function boundGreet() {
return person.greet.call(person);
}class Person {
constructor(name) {
this.name = name;
}
// Regular method
greet() {
console.log("Regular: " + this.name);
}
// Arrow function property
greetArrow = () => {
console.log("Arrow: " + this.name);
}
}
const p = new Person("Ahmed");
// Regular method
const regular = p.greet;
// regular(); // ❌ Error - undefined
// Arrow function
const arrow = p.greetArrow;
arrow(); // ✅ "Arrow: Ahmed" - works!function Car(brand) {
this.brand = brand;
console.log("this =", this);
}
const myCar = new Car("Toyota");
// this = Car {brand: "Toyota"}Inner Working:
new Car("Toyota")
↓
1. {} empty object
2. {}.__proto__ = Car.prototype
3. Car.call({}, "Toyota") // this = {}
4. return {} // thisconst button = document.querySelector('button');
const obj = {
count: 0,
// Wrong way
handleClickWrong() {
console.log(this); // this = button element ❌
this.count++; // Error!
},
// Right way - arrow function
handleClickRight: function() {
setTimeout(() => {
console.log(this); // this = obj ✅
this.count++;
}, 1000);
}
};
// button.addEventListener('click', obj.handleClickWrong); // ❌
// button.addEventListener('click', obj.handleClickRight.bind(obj)); // ✅Arrow functions apni khud ki this nahi banate — parent scope ki this use karte hain.
const obj = {
name: "Object",
regularFunc() {
console.log("Regular:", this.name); // "Object"
function inner() {
console.log("Inner regular:", this.name); // undefined
}
inner();
const innerArrow = () => {
console.log("Inner arrow:", this.name); // "Object"
};
innerArrow();
}
};
obj.regularFunc();Output:
Regular: Object
Inner regular: undefined
Inner arrow: Object
Kyu?
- Regular function apni
thisbanata hai - Arrow function parent ki
thisinherit karta hai
| Situation | this Value |
Example |
|---|---|---|
| Regular function call | undefined (strict) / global | func() |
| Method call | Object before dot | obj.method() |
Constructor call (new) |
New object created | new Class() |
call/apply/bind |
First argument | func.call(obj) |
| Arrow function | Parent scope's this |
() => {} |
| Event listener | Element that triggered event | el.addEventListener(...) |
class Counter {
constructor() {
this.count = 0;
}
// Method 1: Regular method (this can be lost)
incrementRegular() {
this.count++;
console.log("Count:", this.count);
}
// Method 2: Arrow function property (this always bound)
incrementArrow = () => {
this.count++;
console.log("Count:", this.count);
}
}
const counter = new Counter();
// Direct call - dono work karenge
counter.incrementRegular(); // ✅ Count: 1
counter.incrementArrow(); // ✅ Count: 2
// Extracted reference
const regFunc = counter.incrementRegular;
const arrFunc = counter.incrementArrow;
// regFunc(); // ❌ Error - this is undefined
arrFunc(); // ✅ Count: 3 - works!// ❌ Bad Practice - Memory Waste
class Person {
constructor(name) {
this.name = name;
// Har instance ke liye naya function!
this.greet = function() {
console.log("Hi, " + this.name);
};
}
}
const p1 = new Person("Ali");
const p2 = new Person("Sara");
console.log(p1.greet === p2.greet); // false - alag-alag functions!Memory Diagram (Bad):
p1 ──→ { name: "Ali", greet: function } } 2 separate
p2 ──→ { name: "Sara", greet: function } } functions!
// ✅ Good Practice - Shared Method
class Person {
constructor(name) {
this.name = name;
}
// Prototype pe method - shared!
greet() {
console.log("Hi, " + this.name);
}
}
const p1 = new Person("Ali");
const p2 = new Person("Sara");
console.log(p1.greet === p2.greet); // true - same function!Memory Diagram (Good):
p1 ──→ { name: "Ali" } }
} ──→ Person.prototype ──→ { greet: function }
p2 ──→ { name: "Sara" } } (shared!)
Memory Saving:
- 1000 instances = 1000 separate functions (bad) vs 1 shared function (good)
- Shared method saves memory
- Faster object creation
function createObject() {
const obj = {
data: new Array(1000000) // Large array
};
return obj;
}
let myObj = createObject();
// myObj memory mein hai
myObj = null; // Reference khatam
// Ab garbage collector memory free kar degaProcess:
- Object create hota hai → Memory allocate
- Reference lose hota hai → Mark for deletion
- Garbage collector run hota hai → Memory free
function heavyOperation() {
const bigData = new Array(1000000).fill('data');
return {
getData: () => bigData, // Closure - bigData memory mein rahega
size: () => bigData.length
};
}
let result = heavyOperation();
// bigData memory mein hai (closure ki wajah se)
result = null; // Ab memory free hogiMemory Leak Warning:
// ❌ Memory Leak Example
const cache = {};
function addToCache(key, value) {
cache[key] = value; // Kabhi remove nahi hota!
}
// Solution: WeakMap use karo
const cache = new WeakMap();function Person(name) {
this.name = name;
}
// Wrong
const p1 = Person("Ali"); // Forgot `new`
console.log(p1); // undefined
console.log(window.name); // "Ali" - global pollution!
// Right
const p2 = new Person("Sara");
console.log(p2.name); // "Sara"Fix with Safety Check:
function Person(name) {
if (!(this instanceof Person)) {
return new Person(name);
}
this.name = name;
}
const p = Person("Ahmed"); // Works without `new`!// ❌ Never do this!
Array.prototype.myMethod = function() {
return "custom";
};
const arr = [1, 2, 3];
console.log(arr.myMethod()); // Works but dangerous!Kyu galat hai?
- Other libraries ke saath conflict
- Future JavaScript versions mein same name ka method aa sakta hai
- Debugging mushkil ho jati hai
const obj = {
name: "Test",
// ❌ Wrong - arrow function
greet: () => {
console.log(this.name); // `this` is not `obj`!
}
};
obj.greet(); // undefinedSahi tareeqa:
const obj = {
name: "Test",
// ✅ Right - regular method
greet() {
console.log(this.name);
}
};
obj.greet(); // "Test"// ❌ Too deep - fragile and confusing
class A {}
class B extends A {}
class C extends B {}
class D extends C {}
class E extends D {} // Too deep!Better approach - Composition:
// ✅ Composition - flexible
const canWalk = {
walk() { console.log("Walking"); }
};
const canSwim = {
swim() { console.log("Swimming"); }
};
const canFly = {
fly() { console.log("Flying"); }
};
// Duck can walk, swim, and fly
const duck = Object.assign({}, canWalk, canSwim, canFly);
duck.walk(); // "Walking"
duck.swim(); // "Swimming"
duck.fly(); // "Flying"function Parent() {}
Parent.prototype.value = 1;
const child1 = new Parent();
const child2 = new Parent();
// Mistake: Thinking this modifies only child1
child1.value = 2; // Creates new property on child1
console.log(child1.value); // 2 - own property
console.log(child2.value); // 1 - prototype property
// But if we modify prototype:
Parent.prototype.value = 10;
console.log(child1.value); // 2 - own property shadows prototype
console.log(child2.value); // 10 - uses prototype
// Confusing!Samajh:
- Property assign karne se new property ban jati hai (shadowing)
- Prototype change karne se existing objects affected hote hain (jo apni property nahi rakhte)
// Mixin objects with behaviors
const canEat = {
eat(food) {
console.log(`${this.name} is eating ${food}`);
}
};
const canWalk = {
walk() {
console.log(`${this.name} is walking`);
}
};
const canSwim = {
swim() {
console.log(`${this.name} is swimming`);
}
};
// Create objects by mixing behaviors
class Animal {
constructor(name) {
this.name = name;
}
}
class Duck extends Animal {}
Object.assign(Duck.prototype, canEat, canWalk, canSwim);
const duck = new Duck("Donald");
duck.eat("bread"); // "Donald is eating bread"
duck.walk(); // "Donald is walking"
duck.swim(); // "Donald is swimming"Faida:
- Reusable behaviors
- No deep inheritance
- Mix and match as needed
- More flexible than inheritance
// Factory function - decides which object to create
function createAnimal(type, name) {
const animals = {
dog: class Dog {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} says Woof!`;
}
},
cat: class Cat {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} says Meow!`;
}
}
};
const AnimalClass = animals[type];
return new AnimalClass(name);
}
const dog = createAnimal('dog', 'Buddy');
const cat = createAnimal('cat', 'Whiskers');
console.log(dog.speak()); // "Buddy says Woof!"
console.log(cat.speak()); // "Whiskers says Meow!"class Database {
constructor() {
if (Database.instance) {
return Database.instance;
}
this.connection = "Connected";
Database.instance = this;
}
query(sql) {
console.log(`Executing: ${sql}`);
}
}
const db1 = new Database();
const db2 = new Database();
console.log(db1 === db2); // true - same instance!Use case: Jab sirf ek instance chahiye (database connection, config, etc.)
Chalo ab sab concepts ko ek saath use karke ek complete application banate hain:
// Base Class with encapsulation
class Vehicle {
#registrationNumber;
#fuelLevel = 100;
constructor(brand, model, regNumber) {
this.brand = brand;
this.model = model;
this.#registrationNumber = regNumber;
}
// Getter for private field
getRegistration() {
return this.#registrationNumber;
}
// Protected method (convention: _method)
_consumeFuel(amount) {
this.#fuelLevel -= amount;
if (this.#fuelLevel < 0) this.#fuelLevel = 0;
}
refuel() {
this.#fuelLevel = 100;
console.log(`${this.brand} ${this.model} refueled`);
}
getFuelLevel() {
return this.#fuelLevel;
}
// Abstract method (to be overridden)
start() {
throw new Error("start() must be implemented");
}
getInfo() {
return `${this.brand} ${this.model} (${this.getRegistration()})`;
}
}
// Inheritance - Car
class Car extends Vehicle {
constructor(brand, model, regNumber, doors) {
super(brand, model, regNumber);
this.doors = doors;
}
start() {
this._consumeFuel(2);
console.log(`🚗 ${this.getInfo()} started - Fuel: ${this.getFuelLevel()}%`);
}
drive(distance) {
const fuelNeeded = distance * 0.5;
this._consumeFuel(fuelNeeded);
console.log(`Drove ${distance}km - Fuel: ${this.getFuelLevel()}%`);
}
}
// Inheritance - Bike
class Bike extends Vehicle {
constructor(brand, model, regNumber, type) {
super(brand, model, regNumber);
this.type = type; // sports, cruiser, etc
}
start() {
this._consumeFuel(1);
console.log(`🏍️ ${this.getInfo()} started - Fuel: ${this.getFuelLevel()}%`);
}
wheelie() {
console.log(`${this.brand} doing a wheelie! 🤸`);
}
}
// Mixin for electric vehicles
const electricMixin = {
#batteryLevel: 100,
charge() {
this.#batteryLevel = 100;
console.log("🔋 Battery fully charged");
},
getBattery() {
return this.#batteryLevel;
}
};
// Electric Car with mixin
class ElectricCar extends Car {
constructor(brand, model, regNumber, doors) {
super(brand, model, regNumber, doors);
Object.assign(this, electricMixin);
}
start() {
console.log(`⚡ ${this.getInfo()} started silently`);
}
}
// Fleet Management System
class Fleet {
#vehicles = [];
addVehicle(vehicle) {
this.#vehicles.push(vehicle);
console.log(`✅ Added: ${vehicle.getInfo()}`);
}
startAll() {
console.log("\n🚦 Starting all vehicles:");
this.#vehicles.forEach(v => v.start());
}
refuelAll() {
console.log("\n⛽ Refueling all vehicles:");
this.#vehicles.forEach(v => {
if (v.getFuelLevel() < 50) {
v.refuel();
}
});
}
getReport() {
console.log("\n📊 Fleet Report:");
this.#vehicles.forEach(v => {
console.log(`- ${v.getInfo()} | Fuel: ${v.getFuelLevel()}%`);
});
}
}
// Usage - Polymorphism in action
const fleet = new Fleet();
const car1 = new Car("Honda", "Civic", "ABC-123", 4);
const car2 = new Car("Toyota", "Corolla", "XYZ-789", 4);
const bike1 = new Bike("Yamaha", "R1", "BK-001", "sports");
const eCar = new ElectricCar("Tesla", "Model 3", "EV-100", 4);
fleet.addVehicle(car1);
fleet.addVehicle(car2);
fleet.addVehicle(bike1);
fleet.addVehicle(eCar);
fleet.startAll();
// Polymorphism - same method, different behavior
console.log("\n🛣️ Testing individual vehicles:");
car1.drive(50);
bike1.wheelie();
// eCar.charge(); // Electric car specific method
fleet.getReport();
fleet.refuelAll();
fleet.getReport();Output:
✅ Added: Honda Civic (ABC-123)
✅ Added: Toyota Corolla (XYZ-789)
✅ Added: Yamaha R1 (BK-001)
✅ Added: Tesla Model 3 (EV-100)
🚦 Starting all vehicles:
🚗 Honda Civic (ABC-123) started - Fuel: 98%
🚗 Toyota Corolla (XYZ-789) started - Fuel: 98%
🏍️ Yamaha R1 (BK-001) started - Fuel: 99%
⚡ Tesla Model 3 (EV-100) started silently
🛣️ Testing individual vehicles:
Drove 50km - Fuel: 73%
Yamaha doing a wheelie! 🤸
📊 Fleet Report:
- Honda Civic (ABC-123) | Fuel: 73%
- Toyota Corolla (XYZ-789) | Fuel: 98%
- Yamaha R1 (BK-001) | Fuel: 99%
- Tesla Model 3 (EV-100) | Fuel: 100%
⛽ Refueling all vehicles:
Honda Civic refueled
📊 Fleet Report:
- Honda Civic (ABC-123) | Fuel: 100%
- Toyota Corolla (XYZ-789) | Fuel: 98%
- Yamaha R1 (BK-001) | Fuel: 99%
- Tesla Model 3 (EV-100) | Fuel: 100%
Is example mein sab concepts:
- ✅ Encapsulation (private fields)
- ✅ Inheritance (Car, Bike extend Vehicle)
- ✅ Polymorphism (different start() implementations)
- ✅ Abstraction (Fleet manages complexity)
- ✅ Composition (electricMixin)
// 1. Literal
const obj = { prop: "value" };
// 2. Constructor
function Obj() { this.prop = "value"; }
const o = new Obj();
// 3. Class
class Obj {
constructor() { this.prop = "value"; }
}
const o = new Obj();
// 4. Object.create
const proto = { method() {} };
const obj = Object.create(proto);
// 5. Factory
function create() {
return { prop: "value" };
}class Parent {
constructor(x) { this.x = x; }
method() { return this.x; }
}
class Child extends Parent {
constructor(x, y) {
super(x); // Parent constructor
this.y = y;
}
method() {
return super.method() + this.y; // Parent method
}
}class Example {
#private = 0;
increment() {
this.#private++;
}
get value() {
return this.#private;
}
}// Method call
obj.method(); // this = obj
// Standalone
const fn = obj.method;
fn(); // this = undefined
// Fix with bind
const bound = obj.method.bind(obj);
bound(); // this = obj
// Fix with arrow
const arrow = () => obj.method();
arrow(); // this = obj (lexical)- Objects: Data + Methods ka bundle
- Prototype: JavaScript ka inheritance system (classical nahi)
- Classes: Clean syntax, internally prototypes use hote hain
- new keyword: Object banata hai, prototype link karta hai, constructor call karta hai
- this: Context-dependent - kis ne call kiya us pe depend karta hai
- Encapsulation: Private fields (#) ya closures se data hide karo
- Inheritance: extends use karke parent class se inherit karo
- Polymorphism: Same method name, different implementations
- Composition > Inheritance: Deep chains se bacho, mix behaviors
✅ Do:
- Use classes for clarity
- Put methods on prototype (shared)
- Use private fields (#) for sensitive data
- Prefer composition over deep inheritance
- Use arrow functions for callbacks
- Use
super()in child constructors - Check
instanceoffor type checking
❌ Don't:
- Don't define methods in constructor
- Don't modify built-in prototypes
- Don't forget
newwith constructors - Don't use arrow functions as object methods
- Don't create deep inheritance chains (>3 levels)
- Don't access private fields outside class
Apne liye ek Library Management System banao with:
- Book class (title, author, ISBN, available)
- Member class (name, memberID, borrowedBooks)
- Library class (manages books and members)
- Methods: borrowBook(), returnBook(), searchBook()
- Private fields for ISBN and memberID
- Inheritance for different book types (Fiction, NonFiction)
Try karo! 💪