What you were taught was not prototypal inheritance. You were taught how to mimic class inheritance using delegate prototypes. See “Common Misconceptions About Inheritance in JavaScript”.
When people say “favor composition over inheritance” that is short for “favor composition over class inheritance” (the original quote from “Design Patterns” by the Gang of Four) because class inheritance has many flaws and causes many problems. Often people leave off the word class when they talk about class inheritance, which makes it sound like all inheritance is bad — but it’s not.
There are actually several different kinds of inheritance, and only class inheritance is deeply flawed. The rest are OK:
Concatenative inheritance: The process of inheriting features directly from one object to another by copying the source objects properties. In JavaScript, source prototypes are commonly referred to as mixins. Since ES6, this feature has a convenience utility in JavaScript called `Object.assign()`.
Prototype delegation: In JavaScript, an object may have a link to a prototype for delegation. If a property is not found on the object, the lookup is delegated to the delegate prototype, which may have a link to its own delegate prototype, and so on up the chain until you arrive at `Object.prototype`, which is the root delegate.
Functional inheritance: In JavaScript, any function can create an object. When that function is not a constructor (or `class`), it’s called a factory function. Functional inheritance works by producing an object from a factory, and extending the produced object by assigning properties to it directly. Douglas Crockford coined the term, but functional inheritance has been in common use in JavaScript for a long time.
Class inheritance: Rather than inheriting from other objects, classes inherit from classes, and create parent/child object taxonomies as a side-effect. Those taxonomies are virtually impossible to get right from the beginning for all new use cases, and widespread use of a base class leads to the fragile base class problem. In fact, there are many well known problems with class inheritance with well known names:
- The tight coupling problem (class inheritance is the tightest coupling available in oo design), which leads to the next one…
- The fragile base class problem
- Inflexible hierarchy problem (eventually, all evolving hierarchies are wrong for new uses)
- The duplication by necessity problem (due to inflexible hierarchies, new use cases are often shoe-horned in by duplicating, rather than adapting existing code)
- The Gorilla/banana problem (What you wanted was a banana, but what you got was a gorilla holding the banana, and the entire jungle)
I discuss some of the issues in more depth in my talk, “Classical Inheritance is Obsolete: How to Think in Prototypal OO”:
The solution to all of these problems is to favor object composition over class inheritance.
Summed up nicely here: