Start learning free

LESSON 184 OF 344 · OBJECT-ORIENTED PROGRAMMING

Polymorphism in JavaScript: One Call, Many Behaviors

Lesson Read the full explanation

TL ; DR:

Polymorphism is when a single piece of code (like animal.speak()) does different things depending on which subclass the object actually is. You write the call once, and the right override runs automatically.


let animals = [new Dog(), new Cat()];
for (let a of animals) {
    a.speak();   // dispatches to Dog.speak then Cat.speak
}


Full lesson:

Polymorphism (Greek for "many forms") is what makes inheritance and interfaces actually useful at scale. Once Dog and Cat both inherit from Animal and override speak(), you can stop caring which one you're holding. Any code that takes an Animal and calls speak() works for both.

Why it matters

Imagine you're writing a function that prints what every animal in a zoo says:

void Roll(Animal[] animals) {
    foreach (var a in animals) a.Speak();
}

This function works for any current or future kind of animal. Add a Cow subclass tomorrow — this loop runs Cow.Speak() automatically, no edits needed. That's the win: code that works today and keeps working as the type hierarchy grows.

Static type vs dynamic type

The reference is typed as Animal. The object it actually points to is a Dog, or a Cat, etc. Polymorphism is the language dispatching the method call based on the dynamic type (what it is at runtime) rather than the static type (what the variable was declared as).

Notes by language

  • C# / C++ — the parent method must be virtual (and the override marked override) for runtime dispatch. Otherwise you get static dispatch and the wrong version runs.
  • Java / JS / Python — methods are virtual by default. Just override and it works.

Your task

Animal, Dog, and Cat are already defined, with the right overrides. Inside MainFunction, build a collection containing one Dog and one Cat (typed as Animal), then loop through it calling speak() on each.

Expected output:

Woof
Meow
STEP 1 OF 2
    Ctrl + Enter to check

    JAVASCRIPT · OBJECT-ORIENTED PROGRAMMING

    Polymorphism in JavaScript, explained

    What polymorphism is

    Polymorphism comes from Greek words meaning "many forms". In code it means that a single call, such as animal.speak(), does different things depending on which kind of object receives it. You write the call once, and the correct override runs automatically.

    This builds on the previous two lessons. Suppose Dog and Cat both extend Animal and each overrides speak(). Any code that works with animals can call speak() without caring which animal it holds.

    Why it matters

    Consider a loop that makes every animal in a list speak:

    function chorus(animals) {
        for (let a of animals) {
            a.speak();
        }
    }
    

    This function works for every kind of animal that exists today and for kinds that don't exist yet. Add a Cow class tomorrow with its own speak(), and chorus prints Moo for it without a single edit. The code keeps working as the set of classes grows. Without polymorphism you would need a chain of checks, such as "if it is a dog do this, else if it is a cat do that", and each new class would mean changing that chain.

    How JavaScript decides which method runs

    When you write a.speak(), JavaScript looks for speak on the actual object in a, then on its class, then up through the parent classes, and runs the first version it finds. This happens at the moment of the call, so the actual class of the object decides the result. A Dog finds Dog.speak first, and a Cat finds Cat.speak first.

    Statically typed languages separate the declared type of a variable (the static type) from the type of the object it holds at runtime (the dynamic type), and dispatch on the dynamic type. JavaScript variables have no declared type at all, so there is only the object itself. In JavaScript, methods are looked up this way by default, and you need no extra keyword such as virtual.

    If a subclass doesn't override the method, the parent's version runs. With class Fish extends Animal {}, new Fish().speak() prints the base Animal text, which is ... in this lesson.

    Polymorphism without inheritance

    JavaScript doesn't require a shared parent class. The call only needs the object to have a method of that name. An object literal works too:

    let duck = { speak() { console.log("Quack"); } };
    for (let x of [new Dog(), duck]) {
        x.speak();   // Woof, then Quack
    }
    

    This style is often called duck typing: if it has a speak method, it can be used where a speaker is expected. The cost is that a mistake shows up late. An object without the method fails at the call with TypeError: x.speak is not a function. Sharing a parent class keeps the family of objects consistent.

    The exercise, step by step

    The starter code defines Animal, Dog and Cat, each with a speak method. What is missing is the code that uses them, written below the classes at the top level of the file.

    1. Build the list. Declare animals, an array holding a new Dog() and a new Cat(). Arrays can hold any mix of values, and both of these are animals.
    2. Loop and call. Use for...of with a variable a, and call a.speak() inside the loop. The loop has one call site. The dog prints Woof and the cat prints Meow, because each object's own class supplies the method.

    The solution in JavaScript

    class Animal {
        speak() {
            console.log("...");
        }
    }
    
    class Dog extends Animal {
        speak() {
            console.log("Woof");
        }
    }
    
    class Cat extends Animal {
        speak() {
            console.log("Meow");
        }
    }
    
    let animals = [new Dog(), new Cat()];
    for (let a of animals) {
        a.speak();
    }
    

    The output is:

    Woof
    Meow
    

    Common mistakes

    • Checking the class by hand. Writing if (a instanceof Dog) ... else ... inside the loop defeats the purpose. Call the method and let the object decide.
    • Forgetting new in the array. [Dog, Cat] holds the classes themselves, and a.speak() then fails because the class has no such method, only its instances do.
    • Calling speak without parentheses. a.speak evaluates to the function and doesn't run it.
    • Misspelling an override. If Cat defines Speak with a capital letter, the cat falls back to Animal's ..., with no error.
    • Putting the loop above the class definitions. Classes can't be used before their declarations run, so the array has to be created below them.

    Key takeaways

    • Polymorphism lets one call, such as a.speak(), behave differently for different objects.
    • JavaScript picks the method from the object's actual class at the time of the call.
    • New subclasses work with existing code without changing it.
    • A subclass that doesn't override a method uses the parent's version.
    Keep going with JavaScriptAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free