---
title: "16 — Prototypes, Classes, and Iterators — The Object Model, Explained"
uid: iterators-generators-classes
tags: ["classes", "roadmap:javascript", "bind", "prototypes", "generators", "iterators", "apply", "call", "javascript"]
excerpt: "JavaScript has exactly one inheritance mechanism — prototypes — and class, iterators, and generators are all syntax layered on top of that same prototype chain."
date: 2026-08-13T03:28:04+0000
source: https://www.aveshina.my.id/en/blog/iterators-generators-classes
---

"Classes are just sugar over prototypes" was the half-right summary I started with. The idea that everything else hangs off: **JavaScript has exactly one inheritance mechanism — objects linked to other objects via a prototype chain — and class, iterators, and generators are all just syntax layered on top of that same chain.** [1]

The framing that finally landed is the prototype as a *fallback pointer*. Every object has a hidden link to another object — its prototype. When I read a property that isn't on the object itself, JavaScript walks up the prototype chain looking for it, object by object, until it finds the property or reaches the end. That single mechanism — "if not here, ask my prototype" — is the entirety of JavaScript's inheritance. Methods shared by all instances live on the prototype once; each instance holds only its own data and a link upward.

```figure
<svg viewBox="0 0 740 260" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="A prototype chain. Three linked objects left to right: an instance (with its own data), linked by a dashed __proto__ arrow to User.prototype (holding shared methods greet and borrow), linked again to Object.prototype (holding toString, hasOwnProperty). Property lookup walks the chain.">
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">
    <defs>
      <marker id="proto" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto">
        <path d="M0,0 L10,5 L0,10 z" fill="#94a3b8"/>
      </marker>
    </defs>

    <!-- instance -->
    <rect x="20" y="60" width="180" height="150" rx="8" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="110" y="82" font-size="12" font-weight="700" fill="#1e1b4b" text-anchor="middle">instance</text>
    <text x="110" y="105" font-size="9.5" font-family="ui-monospace,monospace" fill="#1e1b4b" text-anchor="middle">name: "Ave"</text>
    <text x="110" y="123" font-size="9.5" font-family="ui-monospace,monospace" fill="#1e1b4b" text-anchor="middle">id: 42</text>
    <text x="110" y="180" font-size="9" font-style="italic" fill="#64748b" text-anchor="middle">own data</text>

    <!-- arrow -->
    <line x1="200" y1="135" x2="260" y2="135" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="5,3" marker-end="url(#proto)"/>
    <text x="230" y="128" font-size="8.5" fill="#64748b" text-anchor="middle">__proto__</text>

    <!-- Class.prototype -->
    <rect x="265" y="60" width="200" height="150" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="365" y="82" font-size="12" font-weight="700" fill="#052e16" text-anchor="middle">User.prototype</text>
    <text x="365" y="108" font-size="9.5" font-family="ui-monospace,monospace" fill="#052e16" text-anchor="middle">greet()</text>
    <text x="365" y="126" font-size="9.5" font-family="ui-monospace,monospace" fill="#052e16" text-anchor="middle">borrow()</text>
    <text x="365" y="180" font-size="9" font-style="italic" fill="#64748b" text-anchor="middle">shared methods</text>

    <!-- arrow -->
    <line x1="465" y1="135" x2="525" y2="135" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="5,3" marker-end="url(#proto)"/>
    <text x="495" y="128" font-size="8.5" fill="#64748b" text-anchor="middle">__proto__</text>

    <!-- Object.prototype -->
    <rect x="530" y="60" width="195" height="150" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="627" y="82" font-size="12" font-weight="700" fill="#422006" text-anchor="middle">Object.prototype</text>
    <text x="627" y="108" font-size="9.5" font-family="ui-monospace,monospace" fill="#422006" text-anchor="middle">toString()</text>
    <text x="627" y="126" font-size="9.5" font-family="ui-monospace,monospace" fill="#422006" text-anchor="middle">hasOwnProperty()</text>
    <text x="627" y="180" font-size="9" font-style="italic" fill="#64748b" text-anchor="middle">root of the chain</text>

    <text x="370" y="240" font-size="9.5" font-style="italic" fill="#64748b" text-anchor="middle">property lookup walks left → right until found</text>
  </g>
</svg>
```

## Prototypes and prototypal inheritance

The mechanism in full: each object has an internal [[Prototype]] slot (exposed in most engines as __proto__, and settable via Object.getPrototypeOf/Object.setPrototypeOf). Reading obj.foo checks obj, then obj.__proto__, then its prototype, up to Object.prototype at the top of the chain [1][2]. Writing *always* sets a property directly on the object, never on the prototype — which is why shadows work: setting obj.foo = 5 on an instance creates an own foo that hides the prototype's foo.

**Prototypal inheritance** is just: create a new object whose prototype is an existing object [2]. Object.create(parent) does exactly this. The new object inherits the parent's properties for free, and can override them without touching the parent. There are no classes in this picture — just objects linked to objects. That's the whole model.

## Classes: the syntax on top

The class keyword, added in ES6, is the ergonomic syntax for this exact mechanism [3]. It *looks* like classical inheritance from Java or Python, but it compiles down to prototypes:

```
class User {
  constructor(name) { this.name = name; }   // sets up own data
  greet() { return `Hi, ${this.name}`; }     // goes on User.prototype
  static create(name) { return new User(name); }  // goes on User itself
}

class Admin extends User {                   // wires Admin.prototype.__proto__ = User.prototype
  constructor(name) { super(name); this.role = "admin"; }
  ban(user) { /* … */ }
}
```

extends links the prototypes; super calls the parent constructor or methods. Underneath, new User("Ave") creates an object whose __proto__ is User.prototype, exactly as if I'd done it by hand. The class is documentation and ergonomics; the prototype chain is what's actually running. That's why class is honestly called "syntactic sugar" — but the sugar matters, because writing extends and super is dramatically clearer than manually wiring Object.create chains.

## Iterators: the protocol

Iteration in JavaScript is also built on a protocol — and the protocol is just an object with a .next() method [4]. The **iterator protocol** says: an iterator is any object with a next() function that returns { value, done }. The **iterable protocol** says: an iterable is any object with a [Symbol.iterator]() method that returns an iterator.

```
const evens = {
  [Symbol.iterator]() {
    let n = 0;
    return {
      next() {
        n += 2;
        return n > 10 ? { value: undefined, done: true } : { value: n, done: false };
      }
    };
  }
};
for (const x of evens) console.log(x);   // 2, 4, 6, 8, 10
```

Arrays, strings, Maps, Sets, TypedArrays, and NodeLists all implement [Symbol.iterator], which is why for...of, the spread operator, and destructuring all work on them uniformly. The protocol is the contract; the loop constructs are just consumers of it.

## Generators: iterators from a function

Writing an iterator by hand is verbose. **Generators** are functions that can pause and resume, and they produce iterators automatically [4]. The function* syntax and the yield keyword do it:

```
function* evens() {
  for (let n = 2; n <= 10; n += 2) {
    yield n;       // pause here, yield the value, resume on next()
  }
}
for (const x of evens()) console.log(x);   // 2, 4, 6, 8, 10
```

Each yield pauses the function and emits a value; calling .next() resumes from where it paused. Generators are the clean way to define lazy or infinite sequences — a generator yielding Fibonacci numbers doesn't compute the next one until asked. They're also the foundation of some async patterns (though async/await largely superseded them there).

## call, apply, bind — explicit this again

These three Function.prototype methods show up in the same neighborhood because they're about *reusing* methods across objects [5][6][7]:

- **fn.call(thisArg, ...args)** — invoke now, this set, args individually.
- **fn.apply(thisArg, [args])** — invoke now, this set, args as an array.
- **fn.bind(thisArg, ...args)** — return a new function with this (and optional preset args) permanently bound.

The connection to prototypes: **function borrowing** — using one object's method against another object by calling it through call/apply, without copying the method. Array.prototype.slice.call(arrayLikeThing) is the classic example of borrowing an array method for an array-like object (though Array.from is the modern equivalent). I covered the binding rules these implement in the this notes; here the point is that they're part of the same object model — functions are values, methods live on prototypes, and call/apply/bind let me invoke a method with any receiver I choose.

## How I use this

Day to day, I write class for anything object-oriented — the prototype chain is doing the work, but the syntax keeps it readable, and extends/super are clearer than manual Object.create. For iteration, I reach for for...of and array methods, and I write a generator (or a [Symbol.iterator] method) only when I need a custom or lazy sequence. call/apply/bind are mostly for fixing this in callbacks — and even there, arrow functions have replaced most of my bind calls. The way of thinking that ties it together — one prototype-chain mechanism, with class/iterator/generator as layered syntax — is what I keep: when something behaves oddly, the answer is almost always "look at where the prototype chain actually points," and Object.getPrototypeOf(obj) answers that in one call.

## References

[1] Mozilla, "Inheritance and the prototype chain," MDN Web Docs, 2024. [Online]. Available: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)

[2] I. Kantor, "Prototypes, inheritance," The Modern JavaScript Tutorial, 2024. [Online]. Available: [https://javascript.info/prototypes](https://javascript.info/prototypes)

[3] I. Kantor, "Classes," The Modern JavaScript Tutorial, 2024. [Online]. Available: [https://javascript.info/classes](https://javascript.info/classes)

[4] I. Kantor, "Iterables and iterators," The Modern JavaScript Tutorial, 2024. [Online]. Available: [https://javascript.info/iterable](https://javascript.info/iterable)

[5] Mozilla, "Function.prototype.call()," MDN Web Docs, 2024. [Online]. Available: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call)

[6] Mozilla, "Function.prototype.apply()," MDN Web Docs, 2024. [Online]. Available: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply)

[7] Mozilla, "Function.prototype.bind()," MDN Web Docs, 2024. [Online]. Available: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)

```quiz
Q: What is the single inheritance mechanism in JavaScript?
- A prototype chain — objects linked to other objects, with property lookup walking the chain
- Classical classes with multiple inheritance
correct: 0
explain: JavaScript has only prototypal inheritance. Every object has an internal [[Prototype]] link; reading a missing property walks up the chain. class syntax is layered on top of this same mechanism.

Q: When you write `class User { greet() {} }`, where does the greet method actually live?
- On User.prototype (each instance's __proto__ points there, so all instances share one copy)
- Copied onto every instance at construction
correct: 0
explain: Methods declared in a class body are placed on the class's prototype object. Instances don't hold their own copy — they inherit it via the prototype chain. This is exactly what you'd do manually pre-class.

Q: An iterator (per the iterator protocol) is any object that…
- has a next() method returning { value, done }
- has a length property and numeric indices
correct: 0
explain: The iterator protocol is the contract: a next() function returning { value, done }. The iterable protocol is having a [Symbol.iterator]() method that returns such an iterator. Arrays, strings, Map, Set all implement it.

Q: What does the yield keyword do inside a generator?
- Pauses the generator, emits a value, and resumes on the next .next() call
- Returns from the generator permanently
correct: 0
explain: yield suspends the generator's execution and produces a value to the caller. The next call to .next() resumes right after the yield. This is what makes generators a clean way to define lazy sequences.

Q: Function borrowing via call/apply means…
- invoking one object's method against a different object (set as this) without copying the method
- making a permanent copy of the method on the target object
correct: 0
explain: fn.call(otherObj) runs fn with this = otherObj. The method isn't copied — it's invoked with a chosen receiver. Array.prototype.slice.call(arrayLike) borrows the array method for a non-array; Array.from is the modern equivalent.
```
