JS

JavaScript Fundamentals

25 lessons

Progress0%
1. Introduction to JavaScript
1What is JavaScript?2Setting Up Your Environment
2. Variables and Data Types
1Declaring Variables2Data Types3Type Conversion
3. Operators
Arithmetic OperatorsComparison OperatorsLogical Operators
4. Control Flow
Conditional StatementsLoops
5. Functions
Function Basics
6. Arrays & Iteration
Array MethodsSpread, Rest & Destructuring
7. Objects & JSON
Working with ObjectsJSON & Optional Chaining
8. OOP & Classes
Class BasicsInheritance & Private Fields
9. Modules & Modern JS
ES ModulesModern JavaScript Features
10. Async JavaScript
PromisesAsync/Await
11. Error Handling
Error Types & try/catchCustom Errors & Debugging
12. Iterators & Advanced
Iterators & GeneratorsMap, Set & WeakRefs
All Tutorials
JavaScriptOOP & Classes
Lesson 17 of 25 min
Chapter 8 · Lesson 2

Inheritance & Private Fields

Inheritance & Private Fields

Inheritance lets one class build on another, reusing and extending behavior. Private class fields, standardized in ES2022, provide true encapsulation within JavaScript classes.

extends

The extends keyword creates a subclass (child class) from a parent class. The subclass inherits all methods from its parent's prototype chain.

js
class Dog extends Animal {
  speak() {
    return `${this.name} barks.`;
  }
}

super

In a subclass constructor, you must call super(...args) before accessing this. This calls the parent's constructor to initialize the inherited portion of the object.

super.method() calls the parent class's version of a method, which is useful when overriding.

js
class GuideDog extends Dog {
  constructor(name, owner) {
    super(name);          // must come first
    this.owner = owner;
  }
  speak() {
    return super.speak() + ` Guide for ${this.owner}.`;
  }
}

Method Overriding

A subclass can redefine a method from its parent. The new definition takes precedence for instances of the subclass.

Private Fields (#)

A field or method prefixed with # is private to the class body. It cannot be accessed or modified from outside the class — not even by subclasses.

js
class Safe {
  #pin;
  constructor(pin) { this.#pin = pin; }
  unlock(attempt) { return attempt === this.#pin; }
}

Trying to access safe.#pin outside the class causes a SyntaxError at parse time, not a runtime error.

instanceof

obj instanceof Class traverses the prototype chain and returns true if Class.prototype appears anywhere in it.

js
const d = new GuideDog("Rex", "Alice");
d instanceof GuideDog // true
d instanceof Dog      // true
d instanceof Animal   // true

Mixin Pattern

JavaScript only supports single inheritance, but you can simulate mixins using functions that extend a base class:

js
const Serializable = (Base) => class extends Base {
  serialize() { return JSON.stringify(this); }
};

Code Examples

extends and superjavascript
class Shape {
  constructor(color = "black") {
    this.color = color;
  }
  area() { return 0; }
  toString() {
    return `${this.constructor.name} [color=${this.color}, area=${this.area().toFixed(2)}]`;
  }
}

class Rectangle extends Shape {
  constructor(width, height, color) {
    super(color);
    this.width  = width;
    this.height = height;
  }
  area() { return this.width * this.height; }
}

class Circle extends Shape {
  constructor(radius, color) {
    super(color);
    this.radius = radius;
  }
  area() { return Math.PI * this.radius ** 2; }
}

const shapes = [
  new Rectangle(5, 3, "red"),
  new Circle(4, "blue"),
  new Rectangle(2, 8, "green"),
];

shapes.forEach((s) => console.log(s.toString()));
console.log(shapes[1] instanceof Shape);

super(color) calls the Shape constructor to initialize the color property. Each subclass overrides area() with its own implementation. Polymorphism lets us call .toString() uniformly across all shape types.

Private Fields (#)javascript
class BankAccount {
  #balance;
  #transactionLog = [];

  constructor(owner, initialBalance) {
    this.owner    = owner;
    this.#balance = initialBalance;
  }

  deposit(amount) {
    this.#balance += amount;
    this.#transactionLog.push(`+${amount}`);
  }

  withdraw(amount) {
    if (amount > this.#balance) throw new Error("Insufficient funds");
    this.#balance -= amount;
    this.#transactionLog.push(`-${amount}`);
  }

  get balance() { return this.#balance; }
  get history() { return [...this.#transactionLog]; }
}

const acct = new BankAccount("Alice", 500);
acct.deposit(200);
acct.withdraw(100);
console.log("Balance:", acct.balance);
console.log("History:", acct.history);

// Cannot access private fields from outside
console.log(typeof acct["#balance"]); // undefined — not accessible

Private fields (#balance, #transactionLog) are inaccessible from outside the class at the syntax level — not just a convention. The getter 'balance' provides controlled read-only access.

instanceof and super.method()javascript
class Animal {
  constructor(name) { this.name = name; }
  speak() { return `${this.name} makes a sound`; }
}

class Dog extends Animal {
  speak() { return `${this.name} barks`; }
}

class ServiceDog extends Dog {
  constructor(name, task) {
    super(name);
    this.task = task;
  }
  speak() {
    return super.speak() + ` and is trained to ${this.task}`;
  }
}

const buddy = new ServiceDog("Buddy", "guide the blind");
console.log(buddy.speak());
console.log(buddy instanceof ServiceDog); // true
console.log(buddy instanceof Dog);        // true
console.log(buddy instanceof Animal);     // true
console.log(buddy instanceof Array);      // false

super.speak() delegates to the parent's implementation, allowing composition of behavior up the chain. instanceof checks the entire prototype chain, so a ServiceDog is also a Dog and an Animal.

Quick Quiz

1. What is required before using 'this' in a subclass constructor?

2. Which of the following correctly defines a private field?

3. If Dog extends Animal and ServiceDog extends Dog, what does (new ServiceDog()) instanceof Animal return?

4. How do you call a parent class method that has been overridden in a subclass?

Was this lesson helpful?

PreviousNext