Prototypes, classes & inheritance
The prototype chain, class syntax, constructors, getters/setters, static and #private members, extends and super.
Most programs model things: users, products, bank accounts, game characters. JavaScript lets many objects share behaviour through prototypes, and the class syntax gives you a clean, familiar way to write them. In this lesson you'll learn both — classes are built on prototypes, so understanding one explains the other.
Prototypes: objects inheriting from objects#
Every object has a hidden link to another object called its prototype. When you read a property that the object doesn't have, JavaScript looks at the prototype, then its prototype, and so on. This is the prototype chain.
You already use prototypes all the time. Array methods live on Array.prototype:
So the chain for an array is nums → Array.prototype → Object.prototype → null. That's why nums.map and nums.hasOwnProperty both work.
Never add methods to built-in prototypes like
Array.prototypein your own code. It can clash with future language features and other libraries.
Classes#
The class keyword is a cleaner syntax for creating objects that share methods through a prototype:
constructorruns when you callnew BankAccount(...)and sets up the instance.- Methods are stored once on
BankAccount.prototypeand shared by every instance. - Classes must be called with
new, always run in strict mode, and aren't hoisted like functions.
Class fields#
Declare instance properties directly in the class body:
Private fields and methods#
Prefix a name with # to make it truly private — enforced by the language:
Before #, developers used an underscore (this._balance) as a polite "please don't touch" — but anything could still access it.
Getters and setters#
Getters are read like properties (t.fahrenheit, no parentheses) — great for derived values and validation.
Static members#
static members belong to the class itself, not to instances. Use them for factory functions, constants and utilities:
Inheritance with extends and super#
A subclass inherits everything from its parent and can add or override behaviour:
describe() in Shape calls this.area(), and each subclass supplies its own version. This is polymorphism: the same call does the right thing for each type.
Extending built-ins
You can extend built-in classes like Error (see the next lesson) or Array:
Under the hood: classes are prototypes#
A class is a function, and its methods live on its prototype:
Before ES2015 you'd write the same thing with a constructor function and assign Dog.prototype.bark = function () {...}. You'll still see that style in older code.
Composition over inheritance#
Deep inheritance trees become rigid: a FlyingSwimmingRobotDog doesn't fit anywhere. Often it's better to compose small pieces:
A good rule: use inheritance for a genuine "is-a" relationship one or two levels deep (a Circle is a Shape), and composition for "has-a" or "can-do" capabilities. Modern front-end code (like React) uses very little inheritance.
Common mistakes#
- Forgetting
new:BankAccount("Ada")throwsClass constructor cannot be invoked without 'new'. - Using
thisbeforesuper()in a subclass constructor. - Passing a method as a callback and losing
this(see the previous lesson). - Putting methods inside the constructor (
this.deposit = function…), creating a new copy for every instance. - Returning internal arrays/objects from getters, letting callers mutate private state.
What's next#
You've already seen throw and RangeError here. Next, learn error handling properly: try/catch/finally, custom error classes and defensive code.
Check your understanding
Quick quiz
1.When you read
obj.propandobjhas no own propertyprop, what does JavaScript do?2.In a subclass constructor, what must you do before using
this?3.What does a
#balancefield in a class give you?
Finished reading?
Mark this lesson complete to track your progress.