cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

Delegation vs classical inheritance

Think of JavaScript prototypes as runtime links between objects, compare them fairly with class-based inheritance, and choose delegation, duck typing, or composition for flexible designs.

By the end, you’ll be able to
  • 01
    Compare object modelsExplain class-based inheritance and JavaScript’s prototype delegation without mixing the metaphors.
  • 02
    Trace delegationPredict which object handles a property or method lookup, including OLOO examples.
  • 03
    Design flexible behaviorUse duck typing and composition when a deep inheritance tree would hurt more than help.

Prototype links, not copies

The summary for this lesson says it plainly: think of prototypes as links between peers, not as copies. When an object does not have a property, JavaScript follows the object’s internal prototype link and asks the next object whether it has the property. Nothing is copied onto the receiver during that lookup.

Earlier in this module, The prototype chain showed the lookup walk, F.prototype & constructors showed how new links instances to a prototype object, Built-in prototypes showed where array and function methods live, and Object.create & null-prototype objects showed how to choose an object’s link directly. This last lesson puts the ideas together as a design tool.

The one-sentence definition

Behavior delegation means an object handles what it owns and delegates missing property lookups to linked objects at runtime.

Real-life analogyClassical inheritance is a family tree of blueprints

In many class-first languages, people picture a family tree of blueprints: a child blueprint extends a parent blueprint, then many finished objects are built from those plans. It is a clear story for stable is-a relationships.

In real life: A parent blueprint
In JavaScript: A base class or constructor prototype
In real life: A child blueprint that specializes the plan
In JavaScript: A subclass or derived constructor
In real life: A finished house built from a plan
In JavaScript: An instance object with its own data

Where the analogy stops: JavaScript can be written with class syntax, but the runtime still uses object links for shared methods. The blueprint story is useful only if you remember the underlying links.

Real-life analogyPrototype delegation is asking a colleague

JavaScript’s prototype model is closer to asking a colleague. If an object cannot answer a request, it asks the object it is linked to. The answer is used, but the method is not copied onto the first object.

In real life: You cannot answer a question
In JavaScript: The object does not have an own property
In real life: You ask the colleague linked to your desk
In JavaScript: Lookup follows [[Prototype]]
In real life: The colleague answers without becoming you
In JavaScript: The method stays on the linked object

Where the analogy stops: A real colleague chooses to help. JavaScript lookup is automatic and follows one prototype link at a time until it finds a property or reaches null.

Classical vs prototype-based OOP

STEP THROUGH

Object-oriented programming groups data with behavior. Class-based OOP usually starts with categories: Widget, then a more specific Button. JavaScript supports that style, especially with class, but the language’s original sharing mechanism is prototype-based: objects linked to other objects.

The experiment below builds the same feature three ways. A Widget has render(). A Button adds clickLabel(). Switch between constructor/prototype inheritance, class extends, and OLOO. The output is identical; the links are what differ.

Same Widget feature, three object models
Step 0 of 7Ready
Your turn: follow the blue line

Switch the implementation, predict the same output, then step through where each method is found.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
  init(name) {    this.name = name;  },  render() {    return "<button>" + this.name + "</button>";  },}; const Button = Object.create(Widget);Button.setup = function (name, label) {  this.init(name);  this.label = label;};Button.clickLabel = function () {  return this.label + " clicked";}; const button = Object.create(Button);button.setup("Save", "Save");console.log(button.render());console.log(button.clickLabel());
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
Switch the implementation shown in the replay

Changing the style starts the replay over. The two printed lines should stay the same.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
Three ways to share behavior
PatternHow behavior is sharedStrengthWatch out
Classical styleConstructor functions or class extends create instances linked to prototype objectsFamiliar for is-a relationships and works with instanceofCan encourage deep hierarchies when behavior really varies by feature
Prototype delegation / OLOOObjects are linked directly with Object.createMatches JavaScript’s lookup model with little ceremonyLess familiar to teams that expect class syntax
CompositionSmall behavior objects or functions are combined into one objectGreat for independent abilities and flexible combinationsName clashes and this assumptions need discipline

Classes come next module in Class basics, followed by Mixins & composition and OOP principles in JavaScript. This lesson does not teach class syntax in depth; it makes the prototype model honest before the class sugar arrives.

Behavior delegation

INTERACTIVE

A delegated method is found somewhere along the prototype chain, but it still runs for the original receiver. In task.describe(), lookup may find describe on TaskActions, yet this inside that method is still task. That is why shared methods can read each object’s own data.

Trace a delegation path
Task object with linked behaviorPop out in the code editor (opens in a new tab)JavaScript
const Formatter = {  formatStatus() {    return this.done ? "done" : "todo";  },}; const TaskActions = Object.create(Formatter);TaskActions.describe = function () {  return this.title + " is " + this.formatStatus();};TaskActions.complete = function () {  this.done = true;}; const task = Object.create(TaskActions);task.title = "Write tests";task.done = false; console.log(task.describe());task.complete();console.log(task.describe());
Lookup pathdescribe
  1. task not here
  2. TaskActions handles it
Before completeWrite tests is todo
After completeWrite tests is done
Try it yourself

Looking for describe checks task → TaskActions. TaskActions handles it.

A conceptual visualizer backed by real objects. It records which object owns the selected method.

Trace it mechanically: start with the receiver before the dot; check own properties; move to the prototype; repeat. If a property is found, JavaScript uses it. If the chain reaches null, the lookup result is undefined. Calling that undefined as a function would then throw a TypeError.

Inherited does not mean copied

If task uses TaskActions.describe, the method still lives on TaskActions. Add an own describe property to task and it shadows the delegated one; it does not edit the shared object.

OLOO: objects linked to other objects

Kyle Simpson’s You Don’t Know JS popularized the name OLOO: Objects Linked to Other Objects. The point is not that classes are evil. The point is that JavaScript’s native model is already about objects linked to other objects, so you can sometimes model behavior directly with plain objects.

OLOO version of the widgetPop out in the code editor (opens in a new tab)JavaScript
const Widget = {  init(name) {    this.name = name;  },  render() {    return "<button>" + this.name + "</button>";  },}; const Button = Object.create(Widget);Button.setup = function (name, label) {  this.init(name);  this.label = label;};Button.clickLabel = function () {  return this.label + " clicked";}; const button = Object.create(Button);button.setup("Save", "Save");console.log(button.render());console.log(button.clickLabel());
Attribution

OLOO comes from Kyle Simpson’s You Don’t Know JS books. It is a teaching name for a pattern JavaScript already supports with Object.create.

Notice the method syntax. The shared Widget methods are inline methods on an object literal, and Button receives its behavior as properties of a linked object. That keeps this calls safe under production minification and keeps the code close to the runtime model.

Duck typing: check the ability

INTERACTIVE

Duck typing is the old phrase: if it walks like a duck and quacks like a duck, treat it as a duck. In code, that means checking for the capability you plan to use instead of checking the family name. If your function needs quack(), check for a callable quack. If it needs to loop over values, check for Symbol.iterator.

Check the ability, not the family name
Duck-typed helpersPop out in the code editor (opens in a new tab)JavaScript
function speakLikeDuck(candidate) {  if (typeof candidate?.quack === "function") {    return candidate.quack();  }  return "not a duck-like object";} function collectValues(candidate) {  if (typeof candidate?.[Symbol.iterator] === "function") {    return [...candidate].join(", ");  }  return "not iterable";} const robotDuck = { quack() { return "beep-quack"; } };console.log(speakLikeDuck(robotDuck));console.log(collectValues(new Set(["waddle", "quack"])));
Capability checksrobot
quack checkbeep-quack
iterable checknot iterable
constructor checknot instanceof Duck
Try it yourself

The lookalike passes because it has a real quack method.

The checks use real optional chaining, typeof, and Symbol.iterator; no arbitrary code is evaluated.

This is also why many JavaScript APIs accept array-like or iterable values instead of only arrays. TypeScript’s structural typing is a related compile-time idea: a value fits when its shape has the needed members, not because it was born in the right class family.

Duck typing is not “call and hope”

Good duck typing checks the exact capability before using it. The lesson code uses optional chaining and typeof so a missing method returns a friendly fallback instead of crashing.

Composition over inheritance

INTERACTIVE

Inheritance asks, “What family is this object in?” Composition asks, “What parts does this object have?” When behaviors combine in many independent ways, composition usually stays flatter and easier to change.

Real-life analogyBuild a robot from interchangeable parts

Instead of inventing a FlyingSwimmingSpeakingThing ancestor, snap together the abilities needed for this one object. Another object can reuse only the parts it needs.

In real life: A flight module
In JavaScript: canFly behavior
In real life: Waterproof fins
In JavaScript: canSwim behavior
In real life: A voice box
In JavaScript: canSpeak behavior
In real life: One robot assembled for a mission
In JavaScript: One object composed from selected abilities

Where the analogy stops: Real robot parts have weight and wiring constraints. JavaScript behavior objects are lighter, but method names can still clash and methods may expect certain data on this.

Build a creature from abilities
Composable abilitiesPop out in the code editor (opens in a new tab)JavaScript
const canFly = {  fly() {    return this.name + " lifts off";  },};const canSwim = {  swim() {    return this.name + " makes waves";  },};const canSpeak = {  speak() {    return this.name + " says hello";  },}; function createCreature(name, abilities) {  return Object.assign({ name }, ...abilities);} const otterBot = createCreature("OtterBot", [canSwim, canSpeak]);console.log(otterBot.swim());console.log(otterBot.speak());
Creature methods2 abilities
nameOtterBot
fly()missing
swim()OtterBot makes waves
speak()OtterBot says hello
Try it yourself

OtterBot currently has swim(), speak(). Those are real own methods copied from the selected ability objects.

Composition builds one object from small behavior parts. If two parts define the same method name, the later part would overwrite the earlier one.

This is the “gorilla holding the banana” problem: you wanted one banana, but inheritance can make you take the gorilla holding it and the jungle behind the gorilla. Composition lets you pass around the banana-sized behavior.

Composition pitfalls

Object.assign copies own properties. If two ability objects both define start, the later one overwrites the earlier one. And if a mixed-in method uses this, call it as a method of the composed object or bind it intentionally.

Where you’ll use this

These ideas show up constantly, even when you are not writing an OOP framework. Built-in arrays delegate methods to Array.prototype. UI objects often share behavior through prototypes or class methods. Plugin systems let a specific plugin delegate missing settings to a defaults object. Utility functions often use duck typing so they can accept arrays, Sets, Maps, strings, or any custom iterable.

Sort these scenarios by the design that fits best. There is no moral winner; the goal is to match the relationship in the problem.

Inheritance, delegation, or composition fits better?
  • A Button is a specialized Widget and should share most widget behavior.
  • A plugin object should ask a shared defaults object for missing behavior at runtime.
  • Some creatures can fly, some swim, some speak, and the combinations change often.
  • A function only needs something with a .quack() method.
  • Several objects should share a method but keep their own data as this.
  • A custom error should be recognized as an Error and as its specific type.
Try it yourself
0 of 6 correct

Sort each scenario by the relationship it suggests: a stable is-a family, a runtime object link, or independent parts.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common misconceptions

“A prototype method is copied into every object.”

It is shared by lookup. Each object keeps its own data; the method stays where it was defined unless you create an own property that shadows it.

“The class keyword means JavaScript stopped using prototypes.”

Class syntax is a nicer way to create constructor functions and prototype methods. The next module teaches the syntax; the lookup model remains prototype-based.

“Delegated methods run with this set to the prototype.”

In obj.method(), this is the original receiver, obj, even if method was found higher in the chain.

“Duck typing means never validating inputs.”

It means validating the capability you actually need, like a callable quack or an iterable protocol method.

“Composition automatically solves design.”

Composition avoids many hierarchy problems, but you still need clear names, conflict rules, and tests for the combinations you build.

Practice: delegation design

5 EXERCISES
Exercise 1 · Warm-upPredict a delegated lookup

Predict the exact console output before you run the program.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const Shared = {
  describe() {
    return this.name + " via " + this.kind;
  },
};
const item = Object.create(Shared);
item.name = "Panel";
item.kind = "delegation";
console.log(item.describe());

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeRewrite a tiny hierarchy in OLOO

    Rewrite the class hierarchy as objects linked to objects.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    class Tool {
      constructor(name) { this.name = name; }
      label() { return "Tool: " + this.name; }
    }
    class Hammer extends Tool {
      swing() { return this.name + " swings"; }
    }
      Exercise 3 · PracticeImplement a duck-typed function

      Write a function that accepts any value with a real quack() method.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      function acceptsQuacker(value) {
        return typeof value?.quack === "function" ? value.quack() : "no quack";
      }
      console.log(acceptsQuacker({ quack() { return "robot quack"; } }));

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeBuild a creature by composing abilities

        Compose one object from canFly and canSwim, then call both methods.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const canFly = { fly() { return this.name + " flies"; } };
        const canSwim = { swim() { return this.name + " swims"; } };
        const duck = Object.assign({ name: "Ducky" }, canFly, canSwim);
        console.log(duck.fly());
        console.log(duck.swim());

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeSpot the deep-inheritance smell

          A game starts with Animal, then adds FlyingAnimal, SwimmingAnimal, and FlyingSwimmingSpeakingAnimal. What pattern would avoid that deep tree?

          Answer, then press Check. Spacing and letter case don’t matter.

            Quiz: check your understanding

            7 QUESTIONS

            Read the code questions by following lookup exactly. Read the design questions by asking what relationship the scenario really has.

            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7What is the key difference between classical inheritance and JavaScript prototype delegation?

              Choose an answer to see the explanation.

            2. Question 2 of 7What does this print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const shared = { role: "shared" };
              const user = Object.create(shared);
              user.name = "Ada";
              console.log(user.role);

              Choose an answer to see the explanation.

            3. Question 3 of 7In an inherited method call like button.render(), what is this inside render?

              Choose an answer to see the explanation.

            4. Question 4 of 7What does OLOO stand for in this lesson?

              Choose an answer to see the explanation.

            5. Question 5 of 7What does this duck-typed check print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              function useIt(value) {
                return typeof value?.quack === "function" ? value.quack() : "skip";
              }
              console.log(useIt({ quack() { return "beep"; } }));

              Choose an answer to see the explanation.

            6. Question 6 of 7Which design is composition over inheritance?

              Choose an answer to see the explanation.

            7. Question 7 of 7What is a common pitfall when mixing behavior objects with Object.assign?

              Choose an answer to see the explanation.

            Key takeaways

            • JavaScript prototypes are runtime links, not copied method bundles.
            • Class syntax is useful, but shared class methods still live on prototypes.
            • OLOO makes object-to-object links explicit with Object.create.
            • Duck typing checks capabilities, such as quack() or Symbol.iterator.
            • Composition builds flexible objects from small behavior parts and avoids deep combination hierarchies.

            Remember the one-liner.
            Delegation means: if this object cannot answer, ask the linked object; composition means: build the object from the behaviors it needs.

            Up next: Class basics. You’ll learn the syntax, now with the prototype model already in your head.

            CompleteFrontend Clear concepts. Working examples.