No You Can't! You Can't Do Prototypal Inheritance in JavaScript!

© 2011, Martin Rinehart

A tiny handful of you are saying, "Yes I can! What's he talking about?" Some small portion of that handful is telling the truth. Congratulations to you.

A bunch of you are saying, "Yes, but I've not learned that part of JavaScript yet." Don't jump the gun. You can write lots of JavaScript, even lots of object-oriented JavaScript, without being able to use inheritance.

And another bunch are saying, "How does he know that? I've tried to learn JavaScript inheritance; I'm no dummy; I've coded lots of OOP in other languages but this one isn't working for me." How do I know? I've been there—done that.

The Prototypal Truth

  1. Inheritance is a key part of object-oriented programming, and therefore it's a key part of JavaScript OOP.
  2. Inheritance in JavaScript is no harder than inheritance in C++, Java, Ruby or any other class-based OOP language.
  3. You can do prototypal inheritance once you dig out from under the avalanche of misinformation that has you buried.

This page is not about how to do prototypal inheritance. It's about why you can't do it. The simple reason you can't do it is that you've studied the subject in supposedly authoritative sources. Those sources were not authoritative. In fact, most didn't know what they were talking about. Let's begin at the beginning.

Why JavaScript "Experts" Get Prototypal Inheritance Wrong

Let's look in on a Java author who got one point right, almost by accident. Early in this century he's writing a book on advanced Java and has come to the disk I/O topic. The libraries have two flavors. Let's call them chocolate and vanilla.

The author has driven 45 minutes from home to a very large bookstore with an excellent selection of the latest Java books. A half dozen of them are piled on the table in front of him. Let's look over his shoulder as he reads:

"Chocolate I/O is primarily for text. Vanilla I/O is used when you have a text/numeric data mixture." The author looks troubled. You see, the chocolate specification provides for a full range of numeric types. "Why," he wonders, "have a full set of numeric types if chocolate is just for text?"

Next book. To the index. To page 378. "For mixing numeric and character I/O, choose vanilla." No explanation of the presence of numeric types in the chocolate spec.

His impatience and concern grow with each additional book. Five books gone, alotted time long gone and one last try before his "advanced" book simply repeats the universal misinformation. But then an odd statement and a glimmer of hope. "You use chocolate I/O to communicate with other applications in your OS. You use vanilla I/O to communicate with other Java applications." The secretarial pad on which he takes notes gets another barely legible scribble. Something to test!

Back at the computer, our author writes a handful of chocolate integers in one file and, in another file, a handful of vanilla. He opens those files with a debugger and yes indeed, the chocolate ones are big-endian; the vanilla ones are little-endian. Book six got this right! Book seven will get it right, but only because our author let his curiousity triumph over his time management.

And Why I Forgive Them

The first generation of Java books, like all first generations, was written by "experts" who were tearing into each new beta release of each Java to piece together enough information to make a book. Here's Rinehart (that would be me, in my second Java book) on Rinehart (that would also be me, from my first Java book).

The Sun JDK documentation suggested that interfaces were some sort of poor person's substitute for multiple inheritance. Lots of us repeated that misinformation. (See my [first Java book] for a typical example of this stupidity.) [emphasis added]

Yes, I got it totally wrong. Sun's documentation, the original soursce, got it totally wrong. If you learned about interfaces from me, or a dozen other authors, you also got it totally wrong. It happens.

That Java author researching chocolate and vanilla was, of course, me. And if you were using Java I/O, your chance of having that totally wrong was depressingly high. So don't pay too much attention to "experts." If a book, page, blog post or article has carefully worked code examples, chances are it's telling the truth. Otherwise ...

The "Experts" on JavaScript's Classes

Unlike any respectable researcher, I'm going to quote other "experts" here but provide zero attributions. My goal is not to slam fellow authors. It's to knock some of the misinformation out of your head, making room, one hopes, for some correct information.

Here are some "expert" opinions: "JavaScript does not support true classes ...". "... includes examples of several JavaScript pseudoclasses and even pseudosubclasses." And, "don't confuse these informal classes with the true classes of JavaScript 2 and other languages."

JavaScript has no classes? I wrote Java from its birth (1995) until last year (2010) and I wrote C++ before Java. I've seen nothing in either language that I couldn't do in JavaScript. In fact, nothing that I couldn't do with less trouble in JavaScript. And I've seen much that is trivial in JavaScript that you wouldn't even attempt in Java or C++. (Did I hear "multiple inheritance"? Yes, a C++ feature that was so questionable that Java dropped it. And you can do it in JavaScript, though I'd definitely recommend against it.)

I do hope that JavaScript 2 doesn't screw up JavaScript when it comes to classes. JavaScript's classes ain't broke. Don't fix 'em.

The "Experts" on new and JavaScript Constructors

"It [the new operator] creates a new object, with no properties ...". There is one small correction: new creates a new object with one and a half properties. The full property is the constructor property. The half property (you can't see it but the prototype business depends on it) is an "implicit" reference (that's what ECMA 262 calls it) to the constructor's prototype property. I'd normally call this picky but both are critical to inheritance in JavaScript.

One expert writes, "You have defined a class of objects simply by defining an appropriate constructor function; ...". This is absolutely correct! The constructor in JavaScript defines a class. The expert who wrote this was on the right track, but only for one sentence in an entire chapter.

The "Experts" on prototype Objects

"... every JavaScript object includes an internal reference to another object, known as its prototype object." Oh dear. That's very easy to misread. There is an internal reference (again, you can't use it) and the property is called, in the ECMA standard, the "prototype" property. This is entirely different, sorry to say, from having a property "named" prototype. Few JavaScript objects have a property named prototype. The ones that do have this property are the ones you need to know.

Countless objects have no prototypes. Object's constructor properties are references to their constructor functions. Those constructor functions may have prototype properties, which is where JavaScript will look for properties that an object lacks. These are the ones you need to know.

The "Experts" on JavaScript Inheritance

Let's sketch a barebones, details ignored class:

function MyClass( params ) {
    /* code here */
}

MyClass.prototype.toString =
    function () {
        return "instance of MyClass";
    }

"The prototype object is associated with the constructor, and each object initialized by the constructor inherits exactly the same set of properties from the prototype." Oh no. It's sad. That is simply not inheritance.

The constructor function, the one referenced by many objects' constructor property provides the basic services of a Java or C++ class. In the above code example, a toString() method is shown, a property of the prototype of the MyClass function. The MyClass() constructor function defines the class, and the toString() method is an instance method of that class: one that every instance of the class can use.

Instance methods are used by their class instances. They may be "inherited" by classes that extend their class. The toString() in the above example is a method of the MyClass class. Every MyClass instance can use it. If another class extends MyClass but doesn't define its own toString() method, then that extending class would inherit the MyClass's toString() method.

OK, I could go on for lots more pages with my collection of quotes from "experts" but I'll call it a day, after I bring up another topic that I've never read about. It's picky. It's immensely important.

The Huge Exception the "Experts" Miss

All JavaScript functions should have a constructor property. An object created by the new operator gets a constructor automatically. An object literal gets a reference to the Object constructor as its constructor property. The JavaScript built-ins (today = new Date()) are no exceptions.

What are exceptions, however, are the DOM-related objects. If you document.createElement() or document.getElementbyId() you have objects that probably don't have constructor properties. They only have constructor properties if the browser authors were exceptionally diligent. (One of five browsers, spring 2011, had exceptionally diligent authors. Congratulations, Opera. As always, test! As usual, keep your hopes up and your expectations down.)

Any DOM objects you collect via DOM traversal methods are other candidates for objects without constructor properties.

So any "expert" who tells you that all objects inherit, directly or indirectly, from Object is wrong. It's just one little, picky point but it may apply to the majority of the objects in most JavaScript code.

By the by, I just discovered this exception late last year. I'd say more on this topic, but I've got to get back to scrubbing all the older stuff I've written. All objects should have a constructor property, but many don't. That means that "all objects inherit from Object" is a misstatement. Everything on the entire World-Wide Web about Object.property is wrong, except for the small amount of my stuff that I've scrubbed. Oh dear. Oh no. I've more work to do. We all do.

Prototypal the Right Way

These are some I've finished scrubbing:
  1. JavaScript Inher . . . Words matter.
  2. Extending Class-Based Classes How the other half lives.
  3. Cascading JavaScript Constructors Getting instance variables right.
  4. Extending Instance Methods in JavaScript Getting methods right.

Feedback: MartinRinehart at gmail dot com

# # #