completefrontend
Open lab My progress
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 two ways to share methodsExplain how classes share methods, and how JavaScript shares them through links between objects.
  • 02
    Follow a lookupPredict which object has the property or method JavaScript finds, including in code that links plain objects.
  • 03
    Pick a design that bendsUse duck typing and composition when a deep family of classes would make the code hard to change.

Linked objects, not copies

In JavaScript, an object can have a hidden link to one other object. That other object is called its prototype. When you ask an object for a property it doesn't have, JavaScript follows the link and asks the prototype. Nothing is copied. The first object only keeps the link.

You met these links in the earlier lessons of this module:

  • The prototype chain showed how JavaScript walks from one object to the next.
  • F.prototype & constructors showed how new links a new object to a prototype.
  • Built-in prototypes showed where array and function methods live.
  • Object.create & null-prototype objects showed how to pick an object's link yourself.

This lesson puts those ideas together. It shows how to use the links to share methods between objects, and when another way works better.

The main idea

Behavior delegation means an object answers for the properties it owns. For a property it doesn't have, it passes the question on to the object it is linked to. To "delegate" means to hand a job to someone else. "Behavior" here means methods: the things an object can do.

An own property, then a linked onePop out in the code editor (opens in a new tab)JavaScript
const shared = { label: "shared" };
const user = Object.create(shared);
user.name = "Asha";
console.log(user.name);
console.log(user.label);
  • Line 1 makes an object called shared.
  • Line 2 makes user with Object.create(shared). This links user to shared, but copies nothing.
  • Line 3 gives user its own name. An own property is one stored on the object itself.
  • Line 4 prints Asha, because user has its own name.
  • Line 5 prints shared. user has no label, so JavaScript follows the link and finds label on shared.
Reading user.label. The user cart holds name: "Asha" but no label. An arrow named [[Prototype]] goes from user to the shared cart, which holds label: "shared". So the read gives "shared".user.labelname: "Asha"user1. no label here[[Prototype]]label: "shared"shared2. found here
user has no label of its own. JavaScript follows the link to shared and reads it there. Nothing is copied onto user.
Real-life analogyClasses are a family tree of blueprints

In many languages that start with classes, people picture a family tree of blueprints. A child blueprint builds on a parent blueprint. Then many finished houses are built from those plans. This picture works well when one thing really is a kind of another, like "a button is a kind of widget".

In real life: A parent blueprint
In JavaScript: A base class, the general plan
In real life: A child blueprint that adds details to the plan
In JavaScript: A subclass, a more specific plan
In real life: A finished house built from a plan
In JavaScript: An object made from a class, with its own data
Real-life analogyDelegation is asking a colleague

You get a question at work that you can't answer. You ask the colleague at the next desk. You get the answer, but your colleague still keeps what they know. You don't become a copy of them.

In real life: You can't answer a question
In JavaScript: The object has no own property with that name
In real life: You ask the colleague at the next desk
In JavaScript: JavaScript follows the prototype link, [[Prototype]]
In real life: The colleague answers, and still knows the answer
In JavaScript: The method stays on the linked object

Classes vs linked objects

STEP THROUGH

Object-oriented programming (OOP) is a way to write code where each object keeps its data and the functions that work on that data together. There are two common ways to share those functions between objects:

  • Class-based: first you describe groups, called classes. For example, a general Widget class, and a more specific Button class built on it. Then you make objects from the classes.
  • Prototype-based: you link objects straight to other objects. An object asks its linked object for anything it doesn't have.

JavaScript can do both. The class keyword gives you the class style. But underneath, JavaScript has always shared methods through prototype links.

The step-through below builds the same feature three ways. A widget has a render() method that returns some HTML. A button adds a clickLabel() method. You can switch between three styles:

  • Constructor functions with a prototype object.
  • class extends, the class style.
  • OLOO, which links plain objects with Object.create. You'll learn the name later in this lesson.

All three print the same two lines. Only the links between the objects are different.

Same widget, three ways to build it
Step 0 of 7Ready
Your turn: follow the blue line

Pick a style, predict the two printed lines, then step through to see where each method is found.

Running in
  1. script
Next: line 1
  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());
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Pick the style shown in the replay

Changing the style starts the replay again. The two printed lines 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.

The table compares the three styles. Read each row as: this is how the style shares methods, this is when it helps, and this is what to watch out for.

Three ways to share methods
StyleHow are methods shared?When does it help?What should you watch?
Classes (or constructors)A constructor function or class extends makes objects that link to a prototype object.It reads well for a stable "is a kind of" family, and instanceof works.It can grow deep family trees when objects really differ by feature.
Linked objects (OLOO)Plain objects link straight to each other with Object.create.It matches how JavaScript looks up properties, with little extra code.Teams that expect class syntax may find it less familiar.
CompositionSmall objects that each hold a few methods are combined into one object.It works well for separate abilities that mix in many ways.Two parts can use the same name, and methods that use this need care.

You'll learn classes in the next module: first Class basics, then Mixins & composition and OOP principles in JavaScript. This lesson doesn't teach class syntax in depth. It shows the prototype links first, so classes make sense when you get there.

Behavior delegation

INTERACTIVE

When you call a method, JavaScript may find it on a linked object. But the method still works on the object you called it on. That object is the one before the dot, and it becomes this inside the method. This is why one shared method can read each object's own data.

An own property hides a linked onePop out in the code editor (opens in a new tab)JavaScript
const shared = { label: "shared" };
const user = Object.create(shared);
user.label = "own";
console.log(user.label);
console.log(Object.hasOwn(user, "label"));

Line 3 gives user its own label. Now line 4 finds the own property first and prints own. We say the own property shadows (hides) the label on shared.

Line 5 uses Object.hasOwn(user, "label"). It asks: is label stored on user itself? It prints true. The link to shared is still there, and shared.label is still "shared".

In the playground below, task links to TaskActions, and TaskActions links to Formatter. Pick a method name. You'll see which objects JavaScript checks, in order, and which one has the method.

Follow a lookup from object to object
A task linked to two method objectsPop 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());
Objects checkeddescribe
  1. task not here
  2. TaskActions has it
Before completeWrite tests is todo
After completeWrite tests is done
Try it yourself

To find describe, JavaScript checks task → TaskActions. TaskActions has it.

These are real objects. The list shows each object JavaScript checks, in order, and which one has the method.
A shared method works on the object before the dotPop out in the code editor (opens in a new tab)JavaScript
const actions = {
  greet() {
    return "Hi, " + this.name;
  },
};
const user = Object.create(actions);
user.name = "Asha";
console.log(user.greet());
  • Lines 1 to 5 put a greet method on actions.
  • Line 6 links user to actions.
  • Line 7 gives user its own name.
  • On line 8, JavaScript finds greet on actions. But user is before the dot, so this is user. The method reads user.name and returns Hi, Asha.
Calling user.greet(). The greet method card is in the actions cart. The user cart holds name: "Asha". Because user is before the dot, this is user, so greet returns Hi, Asha.user.greet()name: "Asha"userthis = user[[Prototype]]greet()actionsgreet found here
JavaScript finds greet on actions. But user is before the dot, so this is user, and the call returns Hi, Asha.

You can follow any lookup by hand, one step at a time:

  1. Start with the object before the dot.
  2. Look for the property on that object itself.
  3. If it isn't there, follow the link to the prototype.
  4. Repeat until you find the property.
  5. If the links end at null first, the result is undefined.

If you then try to call that undefined as a function, JavaScript throws a TypeError. That error means a value was the wrong type for what you tried to do with it.

Using a linked method doesn't copy it

When task uses TaskActions.describe, the method still lives on TaskActions. If you give task its own describe, it hides the linked one for task only. The shared object doesn't change.

OLOO: objects linked to other objects

OLOO stands for Objects Linked to Other Objects. Kyle Simpson made this name popular in his book series You Don't Know JS. The idea is not that classes are bad. The idea is that JavaScript already works by linking objects to objects. So sometimes you can skip classes and link plain objects directly.

The widget built with linked objectsPop 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());

The highlighted lines show three objects and one call:

  • Line 1 makes Widget, a plain object with the shared methods init and render.
  • Line 10 makes Button, linked to Widget.
  • Line 19 makes button, linked to Button.
  • Line 21 calls button.render(). JavaScript checks button, then Button, and finds render on Widget.
A linked object for default settingsPop out in the code editor (opens in a new tab)JavaScript
const defaults = { theme: "light" };
const profile = Object.create(defaults);
profile.name = "Asha";
console.log(profile.theme);
console.log(profile.name);

profile has its own name. It has no theme, so it gets theme from defaults, and line 4 prints light. This is handy for default settings. Later, profile can get its own theme without changing defaults.

Where the name OLOO comes from

The name OLOO comes from Kyle Simpson's You Don't Know JS books. It is a teaching name. It doesn't add anything new to JavaScript: Object.create already does the linking.

Look at how the methods are called. Every call starts with the object before the dot, like button.render(), so this is button. Now say you save the method on its own, with const show = button.render, and call show(). There is no object before the dot, so this no longer points at button. Call the method through its object instead, or use bind. bind makes a new function with this fixed to one object.

Duck typing: check what a value can do

INTERACTIVE

There is an old saying: "If it walks like a duck and quacks like a duck, treat it as a duck." In code, duck typing means you check whether a value can do the job you need. You don't check which constructor or class made it. For example:

  • If your function needs to call quack(), check that quack is a function.
  • If your function needs to loop over a value, check that it has a Symbol.iterator method. That is the method that for...of and spread, [...value], use to loop.
Check what it can do, not where it came from
Two checks for what a value can doPop 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"])));
What each check saysrobot
quack checkbeep-quack
loop checknot iterable
constructor checknot instanceof Duck
Try it yourself

The lookalike passes, because it has a real quack function.

The checks run on real values, with optional chaining, typeof, and Symbol.iterator.
Check for the method you needPop out in the code editor (opens in a new tab)JavaScript
function describe(value) {
  return typeof value?.save === "function" ? value.save() : "cannot save";
}
console.log(describe({ save() { return "saved"; } }));
console.log(describe({ name: "draft" }));

Line 2 uses two tools:

  • value?.save reads save. If value is null or undefined, it gives undefined instead of an error. The ?. is called optional chaining.
  • typeof gives the type of a value as a string, such as "function".

The first object has a save function, so line 4 prints saved. The second object only has a name, so line 5 prints cannot save instead of crashing. The check never asks which constructor made the object.

Left: the first value holds a save function, so describe prints saved. Right: the second value only holds name: draft, so describe prints cannot save.{ save() {…} }save()1st valuehas save()saved{ name: "draft" }name: "draft"2nd valueno savecannot save
describe only asks one thing: is save a function? The first value says yes. The second has no save, so it gets the fallback.

This is why many JavaScript functions accept any value you can loop over, not only arrays. TypeScript has a similar idea, called structural typing. TypeScript checks your code before it runs. A value fits a type when it has the properties the type needs. Which class made the value doesn't matter.

Duck typing still checks first

Good duck typing checks for the exact method before it calls it. It doesn't call the method and hope. The code above uses optional chaining and typeof, so a missing method gives a friendly answer instead of an error.

Composition over inheritance

INTERACTIVE

Inheritance asks: what family is this object in? Composition asks: what parts does this object have? Composition means building one object from small parts, where each part adds one ability. When abilities mix in many different ways, composition keeps your code flatter and easier to change.

Real-life analogyBuild a robot from parts

You build a robot by snapping on parts. You don't invent a new "flying, swimming, speaking robot" family first. You add the parts this robot needs. Another robot can take only the parts it needs.

In real life: A flying part
In JavaScript: The canFly object
In real life: Swimming fins
In JavaScript: The canSwim object
In real life: A voice box
In JavaScript: The canSpeak object
In real life: One robot built for one job
In JavaScript: One object built from the abilities it needs

The playground uses Object.assign. Object.assign(target, ...parts) copies each part's own properties onto the target object, from left to right. Turn abilities on and off to see which methods OtterBot gets.

Build a creature from abilities
Ability objects and a creature builderPop 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 now has swim(), speak(). These are real own methods, copied from the ability objects you picked.

Composition builds one object from small parts. If two parts had the same method name, the later part would replace the earlier one.
Two parts with the same method namePop out in the code editor (opens in a new tab)JavaScript
const canSave = { save() { return "saved"; } };
const canLog = { save() { return "logged"; } };
const tool = Object.assign({}, canSave, canLog);
console.log(tool.save());

Both parts have a method called save. Object.assign copies canSave first and canLog second. The second save replaces the first, so line 4 prints logged.

Object.assign copies the save method from canSave into the tool cart, then copies the save method from canLog over it. The tool cart ends with the canLog save, so tool.save() gives logged.Object.assign({}, canSave, canLog)canSavesave() → savedcanLogsave() → logged1. copied in2. replaces itsave() → loggedtooltool.save() → logged
Object.assign copies the parts from left to right. Both have save, so the second one replaces the first, and tool.save() gives logged.

When you combine parts, give their methods different names, or decide on purpose which one should win.

Composition also keeps each ability next to the feature that needs it. If a screen needs to save but not print, give it only the save part. Don't make it inherit from an object that has both.

Two things to watch with composition

Object.assign copies own properties. If two parts both have start, the later one replaces the earlier one. And if a part's method uses this, call it through the combined object, like robot.start(), or fix this with bind.

Where you'll use this

You'll meet these ideas often, even if you never build your own object system:

  • Arrays get methods like map from Array.prototype, through the link.
  • Parts of a user interface often share methods through prototypes or class methods.
  • A plugin, a small add-on to a bigger program, can take missing settings from a defaults object.
  • Helper functions use duck typing so they work with arrays, Sets, Maps, strings, or any value you can loop over.

Keep shared objects stable. If you change a prototype later, every object linked to it sees the change, and bugs get hard to find. For your app's data, use objects you own. Don't change built-in prototypes such as Array.prototype. If you write a library, say in its docs whether a method still works when it is saved on its own, and which properties it expects on this.

Four questions to pick a style
  • Is one thing a stable kind of another, like a button and a widget? Use inheritance.
  • Does one object mostly give default values to others? Use delegation.
  • Does your function need just one ability from a value? Use duck typing.
  • Are you mixing several separate abilities? Use composition.

Sort these cases by the style that fits best. No style is always right. Pick the one that matches how the objects in the problem relate.

Inheritance, delegation, or composition?
  • A Button is a special kind of Widget and uses most of the widget's methods.
  • A plugin object should get any missing settings or methods from a shared defaults object.
  • Some creatures fly, some swim, some speak, and the mixes change often.
  • A function only needs a value that has a .quack() method.
  • Several objects should share one method, but each keeps its own data as this.
  • A custom error should count as an Error and also as its own special type.
Try it yourself
0 of 6 correct

Put each case where it fits best: a stable family where one thing is a kind of another, a link to another object, or separate 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."

The method stays on the prototype, where it was defined. Each object finds it through the link, and keeps only its own data. An object gets its own version only if you give it an own property with the same name.

"The class keyword means JavaScript stopped using prototypes."

class is a shorter way to write a constructor function and its prototype methods. The next module teaches the syntax. The lookup still follows prototype links.

"A method found on a prototype runs with this set to the prototype."

In obj.method(), this is obj, the object before the dot. That stays true even when method was found further along the links.

"Duck typing means you never check your inputs."

Duck typing does check. It checks for the ability you need, like a quack function or a Symbol.iterator method.

"Composition fixes every design problem on its own."

Composition avoids deep family trees. You still need clear names, a rule for when two parts use the same name, and tests for the combinations you build.

"Changing a prototype only changes one object."

Many objects can link to one prototype. Changing it changes what all of them find. Only share a prototype when you want all those objects to share it.

"Object.assign makes a link."

Object.assign copies own properties onto its target. Object.create makes a link. Use Object.assign when you want copies, and Object.create when you want lookup through a link.

Practice: share methods well

5 EXERCISES
Exercise 1 · Warm-upPredict a linked 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 two small classes as linked objects

    Rewrite these two classes as plain objects linked with Object.create.

    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 · PracticeWrite a duck-typed function

      Write a function that works with any value that has 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 from abilities

        Build 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 a family tree that grew too deep

          A game starts with an Animal class. Then it adds FlyingAnimal, SwimmingAnimal, and FlyingSwimmingSpeakingAnimal. Which style would avoid this deep family tree?

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

            Quiz: check your understanding

            7 QUESTIONS

            For code questions, follow the lookup one step at a time. For design questions, ask how the objects in the question really relate.

            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7What is the main difference between class-based inheritance and JavaScript's 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 button.render(), JavaScript finds render on a prototype. 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 uses composition instead of inheritance?

              Choose an answer to see the explanation.

            7. Question 7 of 7What can go wrong when you combine parts with Object.assign?

              Choose an answer to see the explanation.

            Key takeaways

            • A prototype is a link to another object. JavaScript finds methods through the link; it doesn't copy them.
            • The class syntax is useful, but class methods still live on a prototype.
            • OLOO links plain objects straight to each other with Object.create.
            • Duck typing checks what a value can do, such as having quack() or Symbol.iterator.
            • Composition builds an object from small parts, so you don't need a deep family tree for every mix of abilities.

            Remember the one-liner.
            Delegation means: if this object doesn't have it, ask the linked object. Composition means: build the object from the parts it needs.

            Up next: Class basics. You'll learn the class syntax, and you'll already know the prototype links underneath it.

            CompleteFrontend Clear concepts. Working examples.