Sawalhy.
WritingJavaScript

That [[Prototype]] thing Claude added broke production!

// expand the object, follow the <prototype> rows up

Last post established there are no classes in JavaScript. So it won't shock you that there's no class inheritance either, but what's actually there is more interesting than what's missing.

Why do we even need inheritance? It's for reusing code. But instead of linking to classes (which don't exist in JavaScript), instances of a "class" link to the "class" prototype.

So let's take an object scooby from class Dog. When it tries to call scooby.bark(), the interpreter doesn't go to a set-in-stone class definition like most languages, it goes to scooby's prototype object and looks for that method. If the person writing the code is not a villain, it will be the prototype owned by the class Dog, i.e. Dog.prototype.

If you inspect the prototype object, you'll realise it also has its own prototype object. In the case of Dog.prototype, its prototype is Animal.prototype, which is where the interpreter looks next if it can't find a method definition on Dog's prototype. It's a "prototype chain".

scooby
instance · no methods of its own
.bark()? not here, walk up ↑
Dog.prototype
bark() lives here
found
if not, keep walking ↑
Animal.prototype
eat() lives here
Object.prototype
toString, hasOwnProperty…
null
// inheritance isn't copying, it's delegation to a live object
Okay, you can stop reading now… unless you also want to see the actually interesting JS lore.

What extends hides from you

Pre-ES6 (released in 2015) you had to wire inheritance manually. There wasn't a class syntax, so why would you expect inheritance syntax?

Before I show you the actual code, let's recap the goal. To say that Dog inherits from Animal, we want instances of Dog to share the code we already defined for Animal. So we need to create Dog and have its prototype's prototype (tongue twister, I know) point to Animal's prototype. Sounds simple, right? Not when working with JavaScript 💀

function Animal(name) {
  this.name = name
}
Animal.prototype.eat = function() { /* … */ }

function Dog(name, breed) {
  Animal.call(this, name)   // ③ borrow Animal's setup
  this.breed = breed
}

Dog.prototype = Object.create(Animal.prototype)  // ① link the chain
Dog.prototype.constructor = Dog                  // ② restore the back-reference
Dog.prototype.bark = function() { /* … */ }

Three things stick out:

  1. Setting the prototype using Object.create(). Why aren't we just pointing Dog's prototype at Animal's with Object.setPrototypeOf()? Great question. I'll let the MDN docs answer it: click the link and read the threatening red overlay. When we use Object.create we're giving the class a new prototype rather than redirecting a pre-existing one, which preserves engine optimisations.
  2. Dog.prototype.constructor = Dog. Why set the constructor again? Because Object.create() made an empty object whose only link is Animal's prototype, so constructing a Dog would follow the chain and use Animal's constructor.
  3. Using call() for the Animal constructor. It makes sure the Animal constructor gets a this, because it's being invoked as a plain function call (which doesn't get one assigned, not in any useful way at least).

Even if you made it this far and still don't quite understand what that [[Prototype]] object is: now you know it's a core feature of JavaScript, and not part of the mess you vibe-coded with Claude.

Previous pieceOriginally on LinkedIn