1. Courses
  2. Developer Foundations
  3. JavaScript Deep Dive
  4. Stop 5

35 minIntermediate

Prototypes, classes and modules

Two mechanisms decide how a JavaScript codebase is organised: how objects share behaviour (prototypes, with classes on top) and how files share code (modules). NestJS is built on classes and React Native on modules, so knowing what happens underneath turns confusing errors into quick fixes.

By the end you can

  • Trace a property lookup along the prototype chain and say where a method really lives
  • Write a class with a constructor, getters, private #fields, static members and extends
  • Decide when composition beats inheritance
  • Tell ES modules from CommonJS, and convert a CommonJS project to ESM, fixing each error Node prints
  • Explain circular imports and tree-shaking well enough to spot them in a review

The everyday picture

Every Mangrove store has a copy of the company manual: opening hours, how to greet a customer, how to handle a return. The Riverside store also has a short page of local rules pinned by the till: "we open at 7, not 8, because of the morning commuter crowd". When a new staff member asks "what time do we open?", the manager checks the local page first. If the answer is there, that wins. If not, she looks it up in the company manual.

Nobody photocopies the whole manual into each store's local page. There is one manual, and when head office updates it, every store follows the new rule the next day. That is exactly how JavaScript objects share methods: the prototype is the manual, and the object's own properties are the local page.

The prototype chain

Every object has a hidden link to another object, its . When you read obj.x, JavaScript first looks at obj's . If x is not there, it follows the link and looks in the prototype, then the prototype's prototype, and so on. The first match wins. At the end of the chain is Object.prototype, whose prototype is null; if nothing matched by then, the result is undefined.

JavaScript
// proto.js: one manual, many stores
const franchiseManual = {
  openingHour: 8,
  greet() { return `Welcome to ${this.name}`; },
};
const riverside = Object.create(franchiseManual);   // riverside's prototype is the manual
riverside.name = 'Riverside';
riverside.openingHour = 7;                           // a local rule shadows the manual

console.log(riverside.greet());                      // found on the prototype, this = riverside
console.log(riverside.openingHour);                  // found on riverside itself
console.log(Object.hasOwn(riverside, 'greet'), 'greet' in riverside);
console.log(Object.getPrototypeOf(riverside) === franchiseManual);
console.log(Object.getPrototypeOf(Object.getPrototypeOf(franchiseManual)));   // end of chain
console.log(riverside.closingHour);                  // not found anywhere

franchiseManual.closingHour = 23;                    // change the manual...
console.log(riverside.closingHour);                  // ...and every store sees it
Output
Welcome to Riverside
7
false true
true
null
undefined
23

Three things to take from that output. A property on the object itself the one on the prototype. A method found on the prototype still runs with this set to the object you called it on (Functions, scope and closures), which is why greet says "Riverside". And in checks the whole chain while Object.hasOwn checks only the object; that difference matters when you loop over or validate data.

Quick check

After the code above, you run delete riverside.openingHour. What is riverside.openingHour now?

riverside name: 'Riverside' openingHour: 7 prototype link franchiseManual openingHour: 8 greet() prototype link Object.prototype toString(), … null Lookups .openingHour found at step 1: 7 .greet() found at step 2 .closingHour end: undefined
A property lookup walks the prototype chain. openingHour is found on riverside itself and shadows the manual's 8. greet is found one step up, on the manual. closingHour is not found anywhere, so the walk reaches null and gives undefined.

Loading the rest of the lesson…