OOP Concepts · Practice

OOP in JavaScript

JavaScript’s class keyword looks like C#, but underneath it is still prototypes. Knowing what happens under the hood explains its quirks — and how to share behaviour without inheritance.

Advanced 6 min Lesson 34 of 38 Runnable examplesExamples use modern JavaScript (ES2022 private fields) and C# 13.
Before you start:InheritanceEncapsulation
0/38 in OOP Concepts
On this page · 6 min · Advanced

In simple words#

JavaScript classes are a friendly wrapper around prototypes — like a new label on an old, reliable machine.

class
Syntax for constructor functions and prototypes.
Prototype
The object others inherit from in JS.
#private
Truly private fields in JS classes.

Learning objectives#

  • Explain that JavaScript classes are built on prototypes
  • Compare constructor functions with class syntax
  • Share behaviour with mixins
  • Choose between classes, object literals and factory functions

Explanation#

Every JavaScript object has a hidden link to another object, its prototype. If a property is not found on the object, JavaScript looks at the prototype, then its prototype — the prototype chain.

class (ES2015) is a cleaner way to write constructor functions and prototype methods. typeof SomeClass is "function", and methods live on SomeClass.prototype, shared by all instances.

Modern classes also have #private fields, static members, getters/setters and extends/super — everything this track has used.

JavaScript has single inheritance, but you can mix in behaviour with mixins: functions that take a class and return an extended class.

Not everything needs a class. Object literals ({ x: 1, toString() {...} }) and factory functions that return objects are idiomatic and often simpler.

Code example: Prototypes, classes and mixins#

JavaScriptPrototypes, classes and mixins Runnable
// 1. The old way: a constructor function + prototype
function OldStyleUser(name) {
  this.name = name;
}
OldStyleUser.prototype.greet = function () {
  return `Hi, I am ${this.name}`;
};


// 2. The class way: same thing, nicer syntax
class User {
  constructor(name) { this.name = name; }
  greet() { return `Hi, I am ${this.name}`; }
}


console.log(new OldStyleUser('Asha').greet());
console.log(new User('Ravi').greet());
console.log(typeof User);
console.log(Object.getPrototypeOf(new User('x')) === User.prototype);


// 3. Mixin: add behaviour without a shared parent class
const Timestamped = Base => class extends Base {
  stamp() { return `${this.name} @ 2026-10-28`; }
};
class Admin extends Timestamped(User) {}
console.log(new Admin('Meera').stamp());


// 4. Object literal: an object without any class
const point = { x: 1, y: 2, toString() { return `(${this.x}, ${this.y})`; } };
console.log(`${point}`);
Expected output
Hi, I am Asha
Hi, I am Ravi
function
true
Meera @ 2026-10-28
(1, 2)
Opens this example in the full playground

Step-by-step explanation#

  1. OldStyleUser and User behave the same: both put greet on the prototype, shared by every instance.
  2. typeof User is "function" — a class is a special kind of function.
  3. An instance’s prototype is User.prototype; that is where JavaScript finds greet.
  4. Timestamped is a mixin: it takes any class and returns a subclass with an extra method.
  5. point is a plain object with its own toString — no class needed.

Real-world use case#

React moved from class components to functions and hooks, but libraries such as Angular, NestJS, TypeORM and Phaser are class-based. Knowing both styles lets you read any JavaScript codebase.

Common mistakes#

Expecting class to work like C#

JavaScript classes have no overloading, no interfaces and no access modifiers except #private. Use TypeScript if you want more compile-time checks.

Adding methods in the constructor

this.greet = function () {...} creates a new function for every object. Define methods in the class body so they are shared on the prototype.

Modifying built-in prototypes

Adding methods to Array.prototype can break other code and future JavaScript versions. Write a helper function instead.

Best practices#

  • Use class syntax for OOP in JavaScript
  • Use #private for real encapsulation
  • Prefer composition and mixins over deep extends chains
  • Use plain objects and functions when no shared behaviour is needed

Remember this#

class = nicer syntax for prototypes.

Methods live on the prototype, shared by all instances.

Mixin = function that takes a class and returns a richer class.

Practice exercise#

Your task: Write a Serializable mixin that adds toJSONString() (returns JSON.stringify(this)) to any class. Apply it to a Product class with name and price and log the result.

Need a hint?
const Serializable = Base => class extends Base { toJSONString() { ... } };
Show solution
JavaScript Runnable
const Serializable = Base => class extends Base {
  toJSONString() { return JSON.stringify(this); }
};
class Product {
  constructor(name, price) { this.name = name; this.price = price; }
}
class SerializableProduct extends Serializable(Product) {}
console.log(new SerializableProduct('Pen', 20).toJSONString()); // {"name":"Pen","price":20}
Opens this example in the full playground

Mini quiz#

OOP in JavaScript quizPredict the output Question 1 of 3

What is logged?

class A {}
console.log(typeof A);
Score so far: 0/0

Want more? Practise all OOP Concepts MCQs · OOP Concepts interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.