Free tutorials & notes in Hindi & English · Clean code examples 🧰 Free PDF & Image Tools →
⚡ Days 15–22 – Advanced & Async JavaScript  ·  Day 17

Day 17: this, Classes & Object-Oriented JS

⏱ 100 min📶 Intermediate🎯 100% self-study
Day 17 / 30

✅ Today you will learn

  • The four rules of this
  • call, apply and bind
  • Classes: constructor, methods, getters/setters
  • static members and private # fields
  • Inheritance with extends/super – and when to prefer composition

🏢 In the real world

The cart logic is now needed on the product page, the cart page and the checkout page. Copying functions around leads to three slightly different carts. A Cart class gives one blueprint: every page creates a cart object with the same methods and rules, and its internals stay private so no page can break the totals by accident.

The four rules of this

JavaScript
'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' }));
Welcome to Sharma Sweets Sharma Sweets Lost this: Cannot read properties of undefined (reading 'name') Welcome to Sharma Sweets Welcome to Gupta Bakery
How the function is calledthis is…
obj.method()obj
plain fn() (strict / modules / classes)undefined
Arrow functionWhatever this was where the arrow was written
fn.call(x) / fn.apply(x) / fn.bind(x)x
new Fn() / new Class()The new object
Event listener with a regular functionThe element (event.currentTarget)
💡 Rule of thumb

Use method shorthand for object/class methods, and arrow functions for callbacks inside them – then this almost always means what you expect.

Classes: blueprints for objects

JavaScript
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);
Kaju Katli (₹900/kg) 1350 true Kalakand (₹600/kg) false function true

A real Cart class: static and private fields

JavaScript
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); }
Kaju Katli × 1.5 = ₹1350 Besan Ladoo × 1 = ₹500 Items 2.5 · subtotal ₹1850 · delivery ₹0 · total ₹1850 undefined [] Error: Quantity must be positive
FeatureSyntaxWhy
Private field#items = new Map()Cannot be read or changed from outside – protects totals
Getterget total() { … }Always calculated, never stale
Staticstatic FREE_DELIVERY = 1500Shared setting on the class, not on each cart
Return thisadd() { …; return this; }Chaining: cart.add(a).add(b)
throwthrow new Error(…)Refuse invalid input loudly (Day 18)

Inheritance with extends and super

JavaScript
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);
Assorted Box – ₹720 🎁 (Kaju Katli, Ladoo, Soan Papdi) true true

Deep inheritance

  • Product → Sweet → MilkSweet → BengaliMilkSweet
  • A change at the top breaks many children
  • Hard to model “gift box that is also sugar-free”

Composition ⭐

  • A product HAS tags, a discount, a packaging
  • Combine small objects/functions
  • Flexible – the style React encourages

Real task: using the Cart class in the UI

HTML + JavaScript
<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>
▶  The UI only calls add() and reads getters – rules live inside the class

🏅 Job skills unlocked today

✔ Predict this in methods, arrows, events and bind✔ Write classes with constructors, methods and getters✔ Protect data with private # fields; share settings with static✔ Extend classes and choose composition when better

Practice tasks

🏋️ Practice – do it yourself

Task 1: Create a BankAccount class with a private #balance, deposit(), withdraw() that refuses overdrafts, and a getter balance.
Show solution
JavaScript
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);
Withdrew ₹1200 | Declined: balance ₹3800 | 3800
Task 2: Make a Student class with marks and a getter percentage; extend it into a ScholarshipStudent that adds a bonus.
Task 3: Fix this bug: button.addEventListener("click", cart.clear) – inside clear(), this is not the cart.

Quick quiz

🧠 Quick Quiz – test yourself

1. In obj.method(), this is…
Object before the dot.
2. Arrow functions get this from…
Lexical this.
3. A private class field starts with…
Real privacy.
4. In a child constructor you must call…
Before using this.
5. static properties belong to…
Cart.FREE_DELIVERY.

Interview corner

💼 Interview Corner

How does this work in JavaScript?

this is decided by how a function is called: method call → the object; plain call → undefined in strict mode; new → the new instance; call/apply/bind → the given value; arrow functions do not have their own this and use the enclosing one.

Are JavaScript classes real classes?

They are syntax over prototypes: methods live on Class.prototype and objects link to it. Classes add conveniences like private # fields, static blocks and stricter rules (always strict mode, must use new).

Summary

  • this depends on the call: method, plain, new, bind, arrow.
  • class + constructor + methods + getters model real things.
  • #private fields protect data; static holds shared settings.
  • extends/super for true “is-a”; prefer composition otherwise.
➡️ Next

Day 18: errors and debugging – try…catch, custom errors and finding bugs with DevTools breakpoints.

Frequently Asked Questions

Do I need classes for React?
Modern React uses function components and hooks, so classes are less common there – but OOP thinking and this are still asked in interviews and used in many libraries.
What is the difference between _private and #private?
_name is only a naming convention – anyone can still read it. #name is enforced by the language.
← JavaScript (30 Days)
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।

Next topicDay 18: Errors, try…catch & Debugging Your progress: 17 / 30 (57%) · Lesson 18 / 30 Continue →

How was this lesson?

Your feedback helps us make the content better.

Write a review or suggestion (optional)

After approval, your name and city may appear publicly with your review. Please do not write your email or phone number.

▶ CodingEasily on YouTube

Learn with video lessons in easy Hindi

Featured video: RDBMS Part 3 – Class XI Commerce IT

▶ Subscribe – it’s free