The Illusion of Class 10

This indirection was intended to make the language seem more familiar to classically trained programmers, but failed to do that, as we can see from the very low opinion Java programmers have of JavaScript. JavaScript’s constructor pattern did not appeal to the classical crowd. It also obscured JavaScript’s true prototypal nature. As a result, there are very few programmers who know how to use the language effectively.

~ Douglas Crockford, “Prototypal Inheritance”

The indirection Mr. Crockford refers to here is the Pseudo-Classical Pattern, also known as the Constructor Pattern. This is a way of simulating classical inheritance in JavaScript – a prototypal language.

If it wasn’t obvious from both the title of this post, and from the frankly negative quote above, this is not my favorite object-oriented pattern in JavaScript. I’ll go into this in further detail as I teach the pattern, but the reasons have to do with semantics, usability, verbosity, and blatant indirection. If you don’t pay too much attention to the details, the pattern is actually rather usable. But when you get into the meat of how this pattern simulates classical inheritance from a prototypal system, you see the flaws in the foundation. That said, the pattern can and has been used effectively, and there is one distinct advantage it has over other OO patterns in JS. We’ll get to that later as well.

I’ll be examining the pseudo-classical pattern in comparison with the basic prototypal system. If you don’t know how JavaScript’s prototype system works, then I suggest you read Objects and the Prototype Chain, and come back later.

Otherwise, let’s jump right in.

The Basics

var Point = function(x, y)
{
    this.x = x;
    this.y = y;
}

Point.prototype.translate = function(x, y)
{
    this.x += x;
    this.y += y;
}

var point = new Point(17, 42);
point.translate(5, 6);

Easy enough! But what do we end up with? Well, we end up with a new object of type Point with a prototype equal to Point.prototype. Check it out.

Point
    x: 22
    y: 48
    __proto__: Object
        constructor: function (x, y)
        translate: function (x, y)
        __proto__: Object

We have a Point object, with x and y properties, and a prototype that contains the translate function. Sweet! Now for something a little more complicated.

Inheritance

Unlike a normal classical language, JavaScript has no extends keyword. There is no way for a constructor to directly inherit from another constructor.

Well… sort of. There’s the prototype property that functions have. And when the function is used as a constructor (with the new keyword), you get a new object that has a prototype equal to the function’s prototype property, so a new “instance” of a constructor function will inherit from that function’s prototype property. This is kind of like having an extends keyword.

That last paragraph was probably confusing. And I apologize for that. It really stems from the poorly-chosen naming conventions involved in the pseudo-classical pattern. I’ll discuss this more later, but for now, here’s an example of inheritance in the pseudo-classical pattern.

var Point3d = function(x, y, z)
{
    Point.call(this, x, y);
    this.z = z;
}

Point3d.prototype = new Point();

Point3d.prototype.translate = function(x, y, z)
{
    Point.prototype.translate.call(this, x, y);
    this.z += z;
}

var point3d = new Point3d(19, 20, 21);
point3d.translate(4, 5, 6);

There’s a lot going on here.

First, we need to create our constructor for Point3d. Easy enough; It’s a function, just like Point was in the previous example. We use this just like we did previously, but to avoid code duplication, we can use Point.call – changing the calling context of the Point function to whichever new object we create (referenced by this). The new object, in this way, gets xy, and z properties, just like we want.

Then we determine the inheritance structure by setting Point3d.prototype to a new instance of the Point constructor. All our new Point3d objects will now have a prototype equal to an object with an prototype equal to Point3d.prototype. Did you catch that? There were a lot of different “prototype”s in there, and not all of them mean the same thing.

Lastly, we take advantage of the prototype chain, setting Point3d.prototype.translate to a 3d version of the translate function. In this function, again, we take advantage of the call function to avoid code duplication.

That’s a lot of structure, and the naming gets really confusing at this point. Maybe just looking at the structure will help. This is what our new Point3d object looks like:

Point3d
    x: 23
    y: 25
    z: 27
    __proto__: Point
        translate: function (x, y, z)
        x: undefined
        y: undefined
        __proto__: Object
            constructor: function (x, y)
            translate: function (x, y)
            __proto__: Object

I hope that clears things up a little.

Why Is This So Confusing?

Well, as mentioned at the beginning of this article, constructors were tacked on to the language at the last minute, in hopes of satisfying programmers trained in classical languages (like Java or C++). I can’t imagine much thought was put into it.

This is evident in the naming conventions. Every function has a prototype property, which is used solely for the pseudo-classical style. But it is not the prototype of the function – that would be the __proto__ property (in most JS engines). This indirection causes one really big issue, which was demonstrated above: It makes it really hard to talk about the prototype property versus the prototype of an object. The domains of the two concepts overlap, and when using the pseudo-classical pattern, they are intertwined; The use of functions as constructors depends on the prototype property, and affects the prototype of objects.

This is a big reason why I avoid the pseudo-classical pattern. It removes the prototype property from the equation, and thus reduces the cognitive load required. Ironically, if you understand how it all works, you can forget the details and might be able to take advantage of new as a useful abstraction.

What’s Good?

As much as I dislike this pattern, there are some interesting, and possibly useful parts of it. This, of course, is up for debate, and different people will likely see these in completely different lights.

Familiarity

Many of us “grew up” with classical languages. “Desktop” or “Application” developers may be familiar with Java, C++, or Python, as might developers who studied Computer Science formally. Web developers are likely familiar with PHP or C#. All of these are classical languages, and it can be a boon to see familiar constructs, even if they don’t function quite as they would in the languages we’re used to.

It’s Used Internally

When we want a new Date object, we use new Date. We can also do the same with Array if we choose (though literals are preferred). Using constructors in our own code can keep everything looking the same.

Types

With the pseudo-classical pattern, we can take advantage of instanceof  on our own objects. If you’re into that kind of thing (which you probably shouldn’t be – see the What’s Bad? section, below) point3d instanceof Point will be true.

The Naming Convention

This may be due to my classical education, but I really like capitalizing constructors (Point3d) and lowercasing instances (point3d). It makes naming less of a hassle.

What’s Bad?

Prototype vs. Prototype

This was discussed above. The prototype property of a function is not the function’s prototype, but it could be the prototype of a different object. ‘nuf said.

Familiarity

The use of new might be familiar, but it’s kind of familiar in that Uncanny Valley sort of way. It looks like it’s classical, but it just isn’t. The differences are big enough to cause issues. There is no explicit extends, and there are no real classes (there probably will be, but that’s an issue for a different post). You have to fake it, which is why this is the pseudo-classical pattern.

Verbosity

There’s a lot of x.prototype.y, and even some x.prototype.y.call, and that’s rough on the eyes.

Forgetting new

Constructors are just functions, which means they can be called regularly, without the use of the new keyword. This causes a problem when using this inside of the constructor – it will no longer reference the new object. Instead, it will reference the global object. In the browser, this means setting this.x will set window.x. If you’re diligent, this won’t be a problem. But it could be an interesting bug to find.

Type

In the realm of the browser, there has long been a focus on feature detection, rather than browser detection. It’s considered good practice to check for a feature before using it, rather than make assumptions based on possibly falsified data – the UA string.

The same can be said of checking for type in JavaScript. When using instanceof and typeof with native objects, you’ll run into issues, so it may be a poor decision to get into the habit of using those operators with your own constructs.

It’s far preferable to use Duck Typing, rather than rely on the type of an object. Check for properties, and if they exist, then use them. After all, if it “walks like a duck and swims like a duck and quacks like a duck”, then it might as well be a duck.

But it’s still not going to tell you it’s a duck.

Un-needed Abstraction

The new keyword is an abstraction of two different steps that can be achieve quite easily using a clearer and more prototypal method. Assuming we have our structure already set up, the following pseudo-classical code:

var point3d = new Point3d(19, 20, 21);

Is equivalent to the following code using the initializer pattern (and using the setup/prototype code from that article, rather than this one):

var specificPoint = Object.create(point3d);
specificPoint.init(19, 20, 21);

Is the abstraction really needed? I guess that’s up to you.

Final Thoughts

Crockford was right. The pseudo-classical/constructor pattern is an indirection. It’s confusing, and it prevents people from learning the language effectively.

Underneath, it uses the prototype chain, and it adds some interesting (possibly useful) features on top of it. But if you don’t understand the prototype chain to begin with, then you’re probably not going to be able to use constructors effectively.

About Me

I wear several hats, and I do a lot of different things. By day, I’m a mild-mannered web developer for McKissock Education. By night, I’m CTO and Lead Developer of SlickText.com – Text Message Marketing service. I’m also a musician, songwriter, dancer, and pretty cool guy.

January 29th, 2013 by

10 thoughts on “The Illusion of Class

  1. Reply Lance Roberts Jan 29, 2013 6:31 pm

    Is the 2nd to last code line supposed to be

    var point3d = Object.create(Point3d)

    ?

    • Reply Ryan Kinal Jan 29, 2013 6:40 pm

      Nope! Though the snippet as a whole was wrong.

      In addition to showing the operations that are approximately equivalent to “new Point3d”, I also wanted to show the naming issues that can happen when using the prototypal/initializer patterns.

      The usual naming convention when writing JavaScript is to use a lowercase first letter, and camel case. Since the prototype object (point3d) is likely to be named this way, it makes it more difficult to find a suitable name for an object that has a prototype of point3d.

      My articles about Objects and the Prototype Chain and The Initializer Pattern demonstrate this, as well as providing the “setup” code for that last snippet.

  2. Reply fredrikvestin Jan 30, 2013 10:28 am

    In the last snippet, if you make the init function chainable (it returns ‘this’) then it can all be on one line which might be considered easier and cleaner. It would add some implicit rules as to write the init functions though.

  3. Reply Ray Jan 30, 2013 1:35 pm

    very well written, thanks !

  4. Reply jamesthiele Jan 30, 2013 1:36 pm

    I prefer javascript’s concept of a prototype chain to the use of classes in most object oriented languages.

    Are you aware of the language Self the more or less original prototype base language?

    • Reply Ryan Kinal Jan 30, 2013 1:51 pm

      I’m aware of it, but I haven’t learned anything about it. I’ve heard the Self concept of prototypes is different than the JS concept of prototypes. I’d like to look into it, as I honestly do like the concept of prototypal languages.

  5. Reply Demian Jan 30, 2013 1:49 pm

    Good article. The way JS mixes classical names/patterns like “new” and “constructor” with its prototypical type system makes things really confusing for newcomers that are used to how those work on Java or C# (or many other languages for that matter).

    One quick note though: typeof point3d will return "object", not "Point3d".

    And the problem mentioned on Forgetting new (i.e. that calling a constructor without new can end up setting properties on window instead of raising some error) can be mitigated using "use strict", which i’d ver much recommend using :)

    • Reply Ryan Kinal Jan 30, 2013 2:17 pm

      Good correction! It’s been applied. Not sure what I was thinking there.

      And I’ll probably add a note on “use strict” as well.

  6. Pingback: Links & reads for 2013 Week 5 | Martin's Weekly Curations

  7. Reply Drew Apr 26, 2013 10:59 am

    Pretty great post. I simply stumbled upon your blog and
    wished to say that I have really loved browsing
    your blog posts. In any case I’ll be subscribing in your rss feed and I hope you write again soon!

Leave a Reply