दिन 17: this, Classes और Object-Oriented JS
✅ आज आप सीखेंगे
- this के चार नियम
- call, apply और bind
- Classes: constructor, methods, getters/setters
- static members और private # fields
- extends/super से inheritance – और composition कब बेहतर
🏢 असली दुनिया में
Cart logic अब product page, cart page और checkout page तीनों पर चाहिए। Functions copy करने से तीन थोड़े अलग carts बनते हैं। Cart class एक blueprint देती है: हर page एक जैसे methods और नियमों वाला cart object बनाता है, और उसका अंदरूनी हिस्सा private रहता है ताकि कोई page ग़लती से totals न बिगाड़ सके।
this के चार नियम
'use strict';
const shop = {
name: 'Sharma Sweets',
greet() { return `Welcome to ${this.name}`; }, // 1. method call → this = the object before the dot
later() { return [1].map(() => this.name)[0]; }, // 2. arrow → this from the surrounding method
};
console.log(shop.greet());
console.log(shop.later());
const loose = shop.greet; // 3. plain call → this is undefined (strict mode)
try { loose(); } catch (e) { console.log('Lost this:', e.message); }
const bound = shop.greet.bind(shop); // 4. bind/call/apply set this explicitly
console.log(bound());
console.log(shop.greet.call({ name: 'Gupta Bakery' }));| Function कैसे बुलाया गया | this है… |
|---|---|
| obj.method() | obj |
| साधारण fn() (strict / modules / classes) | undefined |
| Arrow function | जहाँ arrow लिखा वहाँ का this |
| fn.call(x) / fn.apply(x) / fn.bind(x) | x |
| new Fn() / new Class() | नया object |
| Regular function वाला event listener | Element (event.currentTarget) |
Object/class methods के लिए method shorthand, और उनके अंदर callbacks के लिए arrow functions लें – तब this लगभग हमेशा वही होता है जो आप सोचते हैं।
Classes: objects के blueprints
class Product {
constructor(id, name, price, stock = 0) { // runs on new Product(...)
this.id = id;
this.name = name;
this.price = price;
this.stock = stock;
}
get inStock() { return this.stock > 0; } // getter – used like a property
priceFor(kg) { return Math.round(this.price * kg); }
toString() { return `${this.name} (₹${this.price}/kg)`; }
}
const kaju = new Product(1, 'Kaju Katli', 900, 25);
const kalakand = new Product(5, 'Kalakand', 600);
console.log(String(kaju), kaju.priceFor(1.5), kaju.inStock);
console.log(`${kalakand}`, kalakand.inStock);
console.log(typeof Product, kaju instanceof Product);असली Cart class: static और private fields
class Cart {
#items = new Map(); // private field: id → { product, qty }
static FREE_DELIVERY = 1500;
static DELIVERY_CHARGE = 60;
add(product, qty = 1) {
if (qty <= 0) throw new Error('Quantity must be positive');
const line = this.#items.get(product.id);
this.#items.set(product.id, { product, qty: (line?.qty ?? 0) + qty });
return this; // allows chaining: cart.add(a).add(b)
}
remove(id) { this.#items.delete(id); return this; }
get count() { return [...this.#items.values()].reduce((n, l) => n + l.qty, 0); }
get subtotal() { return [...this.#items.values()].reduce((s, l) => s + l.product.price * l.qty, 0); }
get delivery() { return this.subtotal === 0 || this.subtotal >= Cart.FREE_DELIVERY ? 0 : Cart.DELIVERY_CHARGE; }
get total() { return this.subtotal + this.delivery; }
lines() { return [...this.#items.values()].map(l => `${l.product.name} × ${l.qty} = ₹${l.product.price * l.qty}`); }
}
const cart = new Cart();
cart.add({ id: 1, name: 'Kaju Katli', price: 900 }, 1)
.add({ id: 4, name: 'Besan Ladoo', price: 500 })
.add({ id: 1, name: 'Kaju Katli', price: 900 }, 0.5);
console.log(cart.lines().join('\n'));
console.log(`Items ${cart.count} · subtotal ₹${cart.subtotal} · delivery ₹${cart.delivery} · total ₹${cart.total}`);
console.log(cart.items, Object.keys(cart)); // #items is truly private
try { cart.add({ id: 9, name: 'X', price: 1 }, 0); } catch (e) { console.log('Error:', e.message); }| Feature | Syntax | क्यों |
|---|---|---|
| Private field | #items = new Map() | बाहर से पढ़ा या बदला नहीं जा सकता – totals सुरक्षित |
| Getter | get total() { … } | हमेशा गणना, कभी पुराना नहीं |
| Static | static FREE_DELIVERY = 1500 | Class पर साझा setting, हर cart पर नहीं |
| Return this | add() { …; return this; } | Chaining: cart.add(a).add(b) |
| throw | throw new Error(…) | ग़लत input को साफ़ मना करना (Day 18) |
extends और super से inheritance
class Product {
constructor(name, price) { this.name = name; this.price = price; }
label() { return `${this.name} – ₹${this.price}`; }
}
class GiftBox extends Product {
constructor(name, price, contents) {
super(name, price); // call the parent constructor FIRST
this.contents = contents;
}
label() { // override, but reuse the parent version
return `${super.label()} 🎁 (${this.contents.join(', ')})`;
}
}
const box = new GiftBox('Assorted Box', 720, ['Kaju Katli', 'Ladoo', 'Soan Papdi']);
console.log(box.label());
console.log(box instanceof GiftBox, box instanceof Product);गहरी inheritance
- Product → Sweet → MilkSweet → BengaliMilkSweet
- ऊपर का बदलाव कई children तोड़ता है
- “Sugar-free gift box” दर्शाना मुश्किल
Composition ⭐
- Product के पास tags, discount, packaging HAI
- छोटे objects/functions जोड़ें
- Flexible – React यही शैली पसंद करता है
असली task: UI में Cart class
<div id="menu"></div>
<pre id="summary" style="background:#fefce8;padding:10px;border-radius:8px"></pre>
<script>
class Cart {
#items = new Map(); // private field: id → { product, qty }
static FREE_DELIVERY = 1500;
static DELIVERY_CHARGE = 60;
add(product, qty = 1) {
if (qty <= 0) throw new Error('Quantity must be positive');
const line = this.#items.get(product.id);
this.#items.set(product.id, { product, qty: (line?.qty ?? 0) + qty });
return this; // allows chaining: cart.add(a).add(b)
}
remove(id) { this.#items.delete(id); return this; }
get count() { return [...this.#items.values()].reduce((n, l) => n + l.qty, 0); }
get subtotal() { return [...this.#items.values()].reduce((s, l) => s + l.product.price * l.qty, 0); }
get delivery() { return this.subtotal === 0 || this.subtotal >= Cart.FREE_DELIVERY ? 0 : Cart.DELIVERY_CHARGE; }
get total() { return this.subtotal + this.delivery; }
lines() { return [...this.#items.values()].map(l => `${l.product.name} × ${l.qty} = ₹${l.product.price * l.qty}`); }
}
const products = [
{ id: 1, name: 'Kaju Katli', price: 900 },
{ id: 2, name: 'Malai Ghewar', price: 650 },
{ id: 4, name: 'Besan Ladoo', price: 500 },
];
const cart = new Cart();
const menu = document.querySelector('#menu');
for (const p of products) {
const btn = document.createElement('button');
btn.textContent = `+ ${p.name} (₹${p.price})`;
btn.addEventListener('click', () => { cart.add(p, 0.5); show(); });
menu.append(btn, ' ');
}
function show() {
document.querySelector('#summary').textContent = cart.count
? cart.lines().join('\n') + `\n\nSubtotal ₹${cart.subtotal}\nDelivery ₹${cart.delivery}\nTotal ₹${cart.total}`
: 'Cart is empty – add ½ kg of anything';
}
show();
</script>🏅 आज के Job Skills
Practice tasks
🏋️ Practice – ख़ुद करें
Solution देखें
class BankAccount {
#balance = 0;
constructor(owner) { this.owner = owner; }
deposit(amount) { if (amount <= 0) throw new Error('Invalid amount'); this.#balance += amount; return this; }
withdraw(amount) {
if (amount > this.#balance) return `Declined: balance ₹${this.#balance}`;
this.#balance -= amount;
return `Withdrew ₹${amount}`;
}
get balance() { return this.#balance; }
}
const acc = new BankAccount('Riya').deposit(5000);
console.log(acc.withdraw(1200), '|', acc.withdraw(9000), '|', acc.balance);button.addEventListener("click", cart.clear) – clear() के अंदर this cart नहीं है।Quick quiz
🧠 Quick Quiz – ख़ुद को जाँचें
Interview corner
💼 Interview Corner
JavaScript में this कैसे काम करता है?
this इस पर निर्भर है कि function कैसे बुलाया गया: method call → object; साधारण call → strict mode में undefined; new → नया instance; call/apply/bind → दी गई value; arrow functions का अपना this नहीं, वे बाहरी this लेते हैं।
क्या JavaScript classes असली classes हैं?
ये prototypes के ऊपर syntax हैं: methods Class.prototype पर रहते हैं और objects उससे जुड़ते हैं। Classes private # fields, static blocks और सख़्त नियम (हमेशा strict mode, new ज़रूरी) जैसी सुविधाएँ जोड़ती हैं।
सारांश
- this call पर निर्भर: method, साधारण, new, bind, arrow।
- class + constructor + methods + getters असली चीज़ें दर्शाते हैं।
- #private fields data बचाते हैं; static साझा settings रखता है।
- असली “is-a” के लिए extends/super; वरना composition।
Day 18: errors और debugging – try…catch, custom errors और DevTools breakpoints से bugs ढूँढना।
