Javascript’s [[Prototype]] vs Traditional Inheritance

I found THIS great article explaining how is “inheritance” achieved in javascript and I am very happy to share it with you:

OO in JavaScript

In traditional Object-oriented languages, the syntax of classes matches the semantics. You can express the object-oriented concepts of classes, inheritance, and polymorphism directly and explicitly using the language’s syntax. There’s no need to use some helper library to fake your way into OO-like behavior through work-arounds of other language facilities.

JavaScript on the other hand has a set of syntax that looks somewhat OO, but which behaves in frustratingly different ways (which we will cover throughout this article series). As a result, the common way that you implement OO patterns in JS is through any of a variety of user-land helper libraries which let you express the desired semantic relationships between your “objects”. The reason most JS developers use them is because the underlying JS syntax makes those semantic expressions awkward. It’s nice to just let a library handle paving over the confusing syntax hiccups.

Libraries like jQuery are useful because they hide the ugly details of dealing with cross-browser differences in JS engines. But these OO-helper libraries are different: they’re going to great lengths to hide the true nature of JavaScript’s OO mechanisms, instead masking them in a set of patterns that are more familiar to other languages.

At this point of understanding, we should really ask ourselves: is the difficulty of expressing classes and inheritance in pure JavaScript a failure of the language (one which can temporarily be solved with user librariesand ultimately solved by additions to the language like class { .. } syntax), as many devs feel, or is it something deeper? Is it indicative of a more fundamental disparity, that we’re trying to do something in JS that it’sjust not meant to do?

Not everyone drank the JS classes kool-aid, so the rest of this article series will favor a different perspective.

Blueprint

One of the most common metaphors used in traditional class/inheritance OO is that the class represents a “blueprint” for a house to be built, but once you instantiate that class, you are basically copying all the characteristics from the blueprint into the actual built house. This metaphor partially matches, to an extent, what actually happens at a language level when the code is compiled, in that it sort-of flattens the definition of a class (sans “virtual” methods) inheritance hierarchy into the instance.

Of course, a main pillar of inheritance-oriented coding is overriding and polymorphism, which allows an object toautomatically access the most descendant definition for a method, but also to use super-style relative references to access ancestor (aka “virtual”) versions of the same-named method. In those cases, the compiler maintains lookup tables for the virtual methods, but it flattens out the non-virtual parts of the class/inheritance definition. The compiler can determine a lot about what needs to be preserved and not and highly optimize the definition structure it creates in the compiled code.

For our purposes, we can think of traditional class-inheritance as basically a flattening “copy” of behavior down the chain to the instance. Here’s a diagram to illustrate the inheritance relationship between a parent/base classFoo, and child class Bar, and then instances of each, respectively named foo1foo2bar1, andbar2. Visually, the arrows (aka, “copying”) point from left-to-right and top-to-bottom:

Inheritance Arrows

What’s in a name?

Despite the borrowed implications of the common name “prototypal inheritance”, JavaScript’s mechanism works quite differently, which we’ll see in just a moment.

Both definitionally (“…characteristics transmitted from parent to offspring”) and behaviorally (as described above), “inheritance” is most closely associated with the idea of “copying” from parent to child.

When you then take “inheritance” and apply it to a mechanism which has some very different behavior, you are asking for the confusion which has plagued “JavaScript inheritance” documentationeducation, and usage for nearly 2 decades.

To try to wade through this mess, let’s set aside the label “inheritance” and its implications for JS, and hopefully we can arrive at something that is both conceptually more accurate and functionally more useful.

A.B.D’s: Always Be Delegating

JavaScript’s OO-like property mechanism for objects is notated by [[Prototype]], which is the internal characteristic of any object called its prototype-chain — a special link to another object. It’s kind of like a scope mechanism, in that the [[Prototype]] linkage describes which alternate object should be referred to if you request a property or method on your object which doesn’t exist.

In other words, you’re indicating an object to delegate behavior to if that behavior isn’t defined on the object in question.

The above class-oriented Foo and Bar example, expressed in JS, relates object Bar.prototype toFoo.prototype, and then the foo1foo2bar1 and bar2 objects to their respective [[Prototype]]s. The arrows (not copies but live-links) point in a right-to-left, bottom-to-top fashion in JS:

Delegation Arrows

“Behavior delegation” is a more accurate term to describe JavaScript’s [[Prototype]]. This is not just a matter of word semantics, it’s a fundamentally different type of functionality.

If you try to illustrate behavior delegation in terms of the “blueprint” metaphor, you quickly see how it totally breaks down. There’s no way that my home, lacking a guest bedroom, could simply refer to another house, or to the original blueprints, to provide a bedroom for my mother-in-law when she comes to visit. Though the outcomes you can achieve have some respective similarities, the concepts of “inheritance” and “behavior delegation” are quite different.

Some devs insist that “delegation” is just the dynamic version of “inheritance”, like two sides of the same coin, but I see them as orthagonal systems.

How to delegate?

We’ll revisit this later in the article series, but Object.create(..) was added to ES5 to assist with creating an object and then optionally linking its [[Prototype]] to another object. The link that is created is a delegation link, as opposed to an inheritance-by-copy.

Note: Once an object has its [[Prototype]] chain set at its creation, it should for the most part be considered set in stone and not changeable. Technically, browsers which support the __proto__ property, a public representation of the internal link, allow you to change at any time where an object is linked to. However, this practice is littered with landmines and generally frowned upon — it’s almost certainly something you’d want toavoid in your code.

Spade a Spade

You’ve seen how the mechanisms in JavaScript are comparitively different from the mechanisms in other languages. But is it ok to just hand-waive over these differences so we can keep using the term “inheritance” for JS?

The fact is, it’s just not an accurate usage of the term. By insisting that JavaScript has “inheritance”, we’re really saying that the meaning of the word “inheritance” doesn’t matter, or is rather soft.

JS doesn’t statically analyze what parts of an inheritance chain it can safely flatten out and copy, it maintains links to the entire delegation chain throughout runtime, as distinct objects, which means our code can take advantage of a variety of powerful “late binding” dynamic patterns.

If we keep trying to mimic inheritance in JavaScript (syntax hurdles be damned), we get distracted and miss out on all that power that was built into our language from the start.

I say: let’s call it what it is, and stop trying to pile on JavaScript these other concepts that the “inheritance” label implies.

You may also like...