Hacker News new | past | comments | ask | show | jobs | submit login
The world's smallest and fastest classical JavaScript inheritance pattern (github.com/javascript)
91 points by codecurve on Feb 15, 2014 | hide | past | favorite | 54 comments



Weird. As far as I'm concerned, the smallest classical inheritance pattern in javascript is

    Subclass.prototype = Object.create(Superclass.prototype);
And combined with calling your superclasses constructor in your own constructor, that's it. Two lines at most.

You can get a little more complex if you want. In topiarist, I also copy all the 'static' properties from the superconstructor onto the child constructor, check that the prototype hasn't already been modified before replacing it so as to fail fast, and set up the constructor field. But all this stuff is just icing. The smallest classical inheritance pattern is just what I wrote above, and I tend to think that all these articles that make it sound like some deep magic is going on are doing more of a disservice to javascript developers than a service.


nodejs has the util.inherits function which gives you an extra super_ shortcut to the superclass constructor,it makes sense to use it in node.


Quite right, if you're in node, you may as well use util.inherits, especially since it basically does exactly the same thing (plus super_ and constructor):

    exports.inherits = function(ctor, superCtor) {
      ctor.super_ = superCtor;
      ctor.prototype = Object.create(superCtor.prototype, {
        constructor: {
          value: ctor,
          enumerable: false,
          writable: true,
          configurable: true
        }
      });
    };


I've been using this pattern for years and never once ran into a problem. Does anyone here have any compelling reason not to?


I've been doing something like this since 2007 on code bases that run into the many hundreds of thousands of lines of code.

It is actually the correct way to do classical inheritance in javascript, everything else is trimming, convenience, backwards compatibility or is simply wrong. In fact almost every other 'pattern' out there does this at the heart of it.

So, for example you'll see some people use a surrogate instead of doing Object.create (as I did when I first started doing this). That's just doing exactly the same thing as Object.create, but in such a way as to work in pre ecmascript 5 browsers - we didn't have Object.create back then.

http://kybernetikos.com/2007/02/07/inheritance-and-javascrip...


Is that the same as Subclass.prototype = new Superclass()?


no, Object.create() creates the object and sets __proto__ but it does not call the function. "new" also calls the function.


Thanks. Would an empty Superclass constructor result in the same thing?


That's correct. If the Superclass constructor had code in it, then that code would only be run once, regardless of the number of Subclass objects created. So, they would effectively share their super implementations, as if they were just one object. This doesn't matter when the super constructor doesn't do anything, since there is no difference.

The Object.create pattern avoids this by not calling the super constructor at all. If you need the super constructor to be called the way it is in classical inheritance, then you can call Superclass.call(this) in the Subclass constructor along with using Object.create. More details: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...


While this README is very kindly worded, there are several things to be careful of here:

Patching in Object.create with a version that will break third-party code?

https://github.com/javascript/augment/blob/master/lib/augmen...

Extending Object.prototype (!!!) inconsistently between old and new browsers?

https://github.com/javascript/augment/blob/master/lib/augmen...

Just because this is voted to the top of the HN front page ... apparently doesn't mean you don't still need to watch your step.

If you'd like a safe, small and fast function for conveniently setting up the prototype chain (a.k.a. classical inheritance in JavaScript), that works cross-browser, try this one on for size:

    function augment(parent, properties) {
      var child = properties.constructor || function() { 
        return parent.apply(this, arguments); 
      };
    
      var Surrogate = function(){ this.constructor = child; };
      Surrogate.prototype = parent.prototype;
      child.prototype = new Surrogate;
    
      for (var key in properties) {
        child.prototype[key] = properties[key];
      }
  
      return child;
    };
... the nasty bit of JavaScript-specific business there being the intermediate Surrogate, so that you don't need a concrete instance of the parent class to be instantiated, in order to set your prototype chain. Used like so:

    var Person = augment(Model, {
      sortableName: function() {
        return this.lastName + ', ' + this.firstName;
      }
    });


This is nice but not quite right, it only works when overwriting the constructor. Every object in Javascript as a constructor property defined

    var a = {}; a.constructor
    // display function Object() { [native code] }
Therefore the test on `properties.constructor` doesn't work to extend objects without overwriting the constructor e.g.

     var MyString = augment(String, {});
     var myString = new MyString;
     myString
     // display Object {}
     myString.length
     // display undefined
To solve this you need to test if the constructor is not the default one i.e.

    var obj = {};
    var child = properties && properties.constructor && properties.constructor !== obj.constructor ? properties.constructor : function() { 
        return parent.apply(this, arguments); 
      };
Now you can extend objects without providing a new constructor:

    var MyString = augment(String, {});
     var myString = new MyString;
     myString
     // display child {constructor: function}
     myString.length
     // display 0
Nice little utility other than that.


Cool, this is neat. Here is a fuller usage example:

    var Rectangle = augment(Object, {
        constructor: function (width, height) {
            this.height = height;
            this.width = width;
        },
        area: function () {
            return this.width * this.height;
        }
    });
    
    var Square = augment(Rectangle, {
        constructor: function (side) {
            Rectangle.call(this, side, side);
        }
    });
    
    sq = new Square(3)
    //augment.constructor {height: 3, width: 3, constructor: function, area: function}
    //    it will be a little annoying to see augment.constructor  in the console 
    
    sq.width
    // 3
    sq.area()
    // 9
    
    Square.prototype
    // Surrogate {constructor: function, area: function}
    Square.constructor
    // function Function() { [native code] }
    sq instanceof Rectangle
    //true


I was going to write a reply like this:

"Congrats, you just implemented what `extends` does in CoffeeScript!"

And then I saw your username. This is still the best implementation of inheritance in JavaScript that I have seen.


Would anyone mind annotating jashkenas' version of this? For someone like me who isn't yet an advanced Javascript user, I feel this could be a terrific, concise learning opportunity, and while I understand more or less what is going on, I have next to no idea as to the why.


You're in luck. I boiled it down from the slightly more comprehensive version that Backbone uses, annotated here:

http://backbonejs.org/docs/backbone.html#section-206

The main difference being that the Backbone version is also copying "static" properties -- properties defined directly on the parent constructor function -- down into the subclass.


Thanks very much for the pointer!


I still don’t understand why no one does `child.__proto__ = parent;` to inherit properties on the constructor chain too.



Because then your JavaScript will break in both old, and even relatively recent versions of Internet Explorer.


In my humble opinion the smallest classical OOP pattern is as follows:

    function defclass(prototype) {
        var constructor = prototype.constructor;
        constructor.prototype = prototype;
        return constructor;
    }
You may then use it as:

    var Rectangle = defclass({
        constructor: function (width, height) {
            this.height = height;
            this.width = width;
        },
        area: function () {
            return this.width * this.height;
        }
    });
The only problem is that this doesn't support inheritance which is why I created `augment`. While creating `augment` I kept the following things in mind:

1. I didn't want to use a `for..in` loop to copy all the methods of the "class". Hence I used a function instead of an object to model the prototype.

2. Using a function instead of an object to model the prototype is similar to the module pattern. In addition you can create private static properties of a class (such as configuration) easily.

3. I wanted `augment` to work for both the [constructor pattern and the prototypal pattern](http://stackoverflow.com/a/16872315/783743) which is why `base` may be either a function or an object.

4. I wanted `augment` to be able to create singleton instances (again in the spirit of the prototypal pattern of prototypal inheritance). Hence I simply return the prototype if the "class" doesn't have a constructor.

Keeping these points in mind I added 4 more lines to the `defclass` function to create `augment`:

    function augment(base, body) {
        var uber = typeof base === "function" ? base.prototype : base;
        var prototype = Object.create(uber);
        body.apply(prototype, [].slice.call(arguments, 2).concat(uber));
        if (!prototype.hasOwnProperty("constructor")) return prototype;
        var constructor = prototype.constructor;
        constructor.prototype = prototype;
        return constructor;
    }
The above function is essentially the `augment` function in my GitHub repository minus the following features:

1. It's not defined as a method of `Function.prototype` or `Object.prototype`. Hence you can't call it as a method on a function or an object. I added this feature for flexibility of style. In retrospect I believe that it's unnecessary and hence I'll remove it.

2. It won't work in browsers which don't support and haven't patched `Object.create`. I understand that the patch my library provides is incomplete. In my defense I never thought that it would become infamous overnight. I'll fix this as well.

3. It doesn't use `bindable`, `callable` and other functional features I commonly use in my code. When I created `augment` I never expected many people to use it. Hence I put in all the functional features I commonly use in all my programs.

4. It doesn't use the `{}.hasOwnProperty.call` hack. Again I programmed defensively in my library. However I doubt that JavaScript programmers usually create classes which don't inherit from `Object.prototype`. Hence I'll remove that hack as well.

In short I'll remove all the unnecessary features from `augment` that I commonly use and deliver the smallest classical OOP library as promised.


A bit OT and naive question maybe but when do you need OOP in JavaScript? I code a lot of web stuff but the only time I've used OOP in JS is when I make games (where inheritance etc makes sense). Does anyone have a real-world example?


Naive questions are often the best questions. You'd be smart not to let go of that one.

The terms "object" and "object-oriented" mean so many different things that it's hard for any discussion not to run aground on semantics. But I'll chip in with this anyway: in my experience, you don't need OOP, and it's a particularly obscuring influence in JavaScript. Certainly you should not take for granted that OOP reduces complexity, but rather ask yourself in the context of particular programs how true that really is. (Games may be a special case, since they're closer to the simulation-of-an-external-system paradigm that is the sweet spot for OOP.)

In my view, the fundamental problem with OOP may be premature encapsulation. Once the core concepts of a program have gotten clear enough to be very well-defined, it makes sense to put them in the kiln and bake them. The rest of the code can then just use an interface and care nothing about the details. But as long as the core concepts and their relationships are still in the process of being discovered, which is actually the default case for most programming, what you need most is malleability. The OO infrastructure of encapsulation impedes that. This manifests as unclear classes with unclear names and unclear dependencies between them. Such a program is harder to work with than a loose collection of functions would be. And the fact that JS wasn't designed to be used this way makes its effects worse in JS.


    > I code a lot of web stuff but the only time 
    > I've used OOP in JS is when I make games
That's very doubtful. Ever used a String in JavaScript? Ever used an Array? Ever used a regular expression? How about a function? All of those things are objects in JavaScript — and are used and useful as such.

The notion that OOP === single-inheritance needs to be put out of its misery. An object is anything that binds together data and code in a single value, and you use them all the time.


Your first paragraph is true of functional languages too, which are decidedly not OO by any modern definition. OOP is about inheritance and encapsulating mutable state.


An object is by definition data with associated functions to operate on that data. One might say that functional programming couples functions(methods) with data, while object oriented programs couple data with methods(functions).

In an object oriented paradigm, you send messages between objects, in a functional paradigm you pass data to functions. But, functions can normally take functions as arguments as well -- and all in all you can do one thing in the other -- but I think the big difference is on whether you model "smart data" or operations/functions.

Classes comes in when you have many objects that share behaviour. That can be modelled as traits/interfaces or as classes (grouping methods, and possibly singleton state). These things can be inherited, or assigned (as in javascript/self).


I think he meant implementing at least two of his own objects where one inherits from the other.


Using OOP in JS is not necessary per se, but when writing complex apps it helps to keep things clean.

Say you're working on a twitter clone for example. It'll make your life much easier if there's a "NewsFeed" class that contains an array of instances of the class "News", which when clicked on shows an instance of "NewsDetails". (All of these would be "Views" if you used if you used an MVC framework like Backbone, probably backed by a model class that contains news data like the tweet content, the # of re-tweets, etc.)

You could do all of the above without using objects; it's just that your code will be a lot messier IMO.


OOP doesn't necessarily imply inheritance, I use composition quite a lot in JS because it tends to make code much more readable for others. Complex object hierarchies in prototype-based languages can become very confusing very quickly.


When you're writing an MVC app it might make sense to inherit from something like a Model or a View.


Dont worry, once Javascript get classes you'll have all these design pattern talibans on your back saying your code isnt java like / SOLID / DRY / YAGNI / enough.

Ironically the PHP community suffers from this problem since it has explicit classes and interfaces,while being a design mess under the hood.

About OO and JS, a lot of projects actually use prototypal inheritance , you just dont see it because it's hidden under a jquery like facade/chainable API, where you never need to use the new keyword explicitly. It's obvious that libs that use these kind of DSL are fairly popular in the js world,more than the one using "explicit" OO.


When creating complex apps, by the book design pattern solutions are nice to have, e.g: www.sharefest.me, www.asana.com and so on.




Wait a second. I thought you made a library for classical inheritance in JavaScript. What's all this functional and Array#from crap you're trying to sell with it? Do one thing and do it well.

Another thing that sits a little weird with me is that this is in the "javascript" organization on GitHub. I think that's a bit misleading, people might mistake this as some official JS library.


JavaScript inheritance library? JS does this by default, no need for more useless boilerplate. The below works in all browsers, IE5 included.

function inherits(child, parent) { function tmp() {}; tmp.prototype = parent.prototype; child.prototype = new tmp(); child.prototype.constructor = child; }

function A() { this.x_ = 5 }; function B() {A.call(this);};inherits(B, A);


> var bindable = Function.bindable = bind.bind(bind);

The latter part of this line needs a bit of documentation.


This is cool, but I like jashkenas' version in the comments here better.

As much as anything, this is because I maintain a reflexive aversion to touching `Object.prototype`.

If you're disciplined about avoiding this you can use `for ... in` loops without having to dick around with testing whether the properties are actually directly on your object every time, or writing a wrapper that takes a callback.


This is pretty neat. A minor nitpick:

>You can easily include it in fiddles and benchmarks using the following HTML code...

You really shouldn't use raw.github.com like that. If you're doing something to show a couple of people, or for your own testing, you could use rawgithub.com instead, but otherwise, you should just upload it somewhere reliable.


PJs https://github.com/jayferd/pjs is the sanest js classical inheritance library I've seen. It's very lightweight, doesn't mess with Object.prototype, and interoperates seamlessly with coffeescript classes.


It's interesting that so much time and effort is spent by JavaScript developers implementing classical inheritence when this is a concept increasingly shunned in classically typed languages.


Languages like C++, Java, C#, Objective-C, Python and Ruby make inheritance dead simple to use. There's one main way of doing it, it's very natural, and so it can often be used excessively.

JavaScript makes inheritance a hassle. There are numerous ways of faking it, and they aren't always compatible with one another.

I don't think we're seeing inheritance "shunned" in those so-called "classical OO" languages. Given more experience with the potential pitfalls of overuse of inheritance, I do think we're seeing it used less extensively than it once was, but it's still a useful technique.

On the other hand, we do see JavaScript developers putting a lot of time and effort into trying to add functionality that is inherently useful, but that isn't offered well by default. It's the opposite of the problem that those other languages suffered from. They make it too easy by default; JavaScript makes it too difficult.


Or maybe not so much too difficult, as pathologically convoluted.


I think it's almost like a challenge people do for its own sake because it's something even Crockford failed to do correctly. Write an awesome classical inheritance pattern, write a bunch of tests to convince yourself it works, and then ignore it.

Indeed, the fact Crockford's stuff on classical inheritance in The Good Bits was wrong but the book got (and retains) so much traction indicates just how much no-one cares.


Could you link to a good explanation of how that stuff fails please? Not doubting you, just want to read more.


It's been a while since I used it, and I can't remember what the problems were off the top of my head but they were face palm level.

Here's a discussion on stack exchange:

http://programmers.stackexchange.com/questions/173176/javasc...

The general problem with all classical inheritance patterns in Javascript is that they don't really work (they treat Javascript as a static language so you get all kinds of nasty surprises because it isn't). This is particularly sad in comparison with older, conceptually simple languages like Objective-C that do this stuff properly.

Crockford himself says it was a mistake to even include the section in the book (in 8 years he's never used it)

http://www.crockford.com/javascript/inheritance.html

The takeaway point is that doing classical inheritance in Javascript is a Bad Idea. If you think you've done it, you probably haven't. And no-one will use it.


The Good Bits had way more stuff than "how to write classical inheritance". Indeed, IIRC, the section right after that was "how to write prototypical inheritance".


Of course. There's lots of great stuff in Good Bits but the fact it had egregious errors in its section on classical inheritance and no-one really noticed says a good deal about how much it matters for Javascript programmers.


>when this is a concept increasingly shunned in classically typed languages.

It's only shunned by a certain class of hipster and more-functional-than-thou programmers. The vast majority of classically typed language programmers still use classical inheritance.


No, there's just not that many true instances of the is_a relationship outside of shapes. Reuse is often better achieved via composition rather than complex up-and-then-down-again virtual call chains.


>No, there's just not that many true instances of the is_a relationship outside of shapes.

I was talking about if the majority shuns inheritance or not [it does not]. Not whether it's the correct way to go.

That said, there's lots of instances of is_a relationship. Anywhere where you have different types of something.

A work hierarchy for example. While composition might work for the methods we want to have, conceptually a manager is not someone who HAS an employee (composition), he IS-AN empoyee.

GUI widgets. Game sprites. Vehicles. Animals. Simulation objects.

Consider how the "semantic web" is all about taxonomies -- put everything into taxonomies.


It's shunned in most languages because it's often the only way to do things to offer. OOP is really appropriate in some circumstances but it's not the ultimate paradigm that solves all your problems. Javascript offers some facilities that makes it more polyvalent.

Disclaimer : I'm not actually a big fan of javascript.


this, let's implement inheritance movement nonsense is crufts of Java and C++. (and only C++ can be forgiven, for performance reasons.)

Python has proven that a successful dynamic language should have duck-typing, via what called late-binding. you perform lookups on a live object. And this makes even more sense for javascript, which intentionally violates whole notion of typing.

In python, yes, there are such things like 'abstract base classes', but they're mostly for convenience, not forced like interfaces in Java. which means no one write codes like:

  If not isinstance(obj, JavaicMentalMasturbationBase):
    raise TypeError, "rogue object does not follow holy Javaland commandments: %r" %obj
instead, they exist that a framework provider can tell developers, on what are the possible objects that can be fed to their framework. e.g) to use our SessionInterface, your custom session has to provide get_session and save_sesion methods.

It seems like Branden Eich's original intention has came to fruition - create a sub-par language and name it after Java. sub-sub-par developers from Java will like the language, it even has closures! But I think he didn't expected Java idiots to force their idioms and harm the whole ecosystem - usually, enterprisey code monkeys were very silent in their cubicles.


>Python has proven that a successful dynamic language should have duck-typing, via what called late-binding. You perform lookups on a live object.

It has proven nothing of the short. For one, other languages have done it before (and much better, like Smalltalk). Second, it's not something that, in Python's implementation, makes it particularly suited or pleasant for large codebases.

>e.g) to use our SessionInterface, your custom session has to provide get_session and save_sesion methods.

So, relegate work that the computer can do to the programmer. Make him do tedious and error-prone housekeeping.

The rest of the comment reads like a teenager's attempt at sounding like "cool" and in the know. Things line "JavaicMentalMasturbationBase", "create a sub-par language and name it after Java", "Java idiots" and the like.

I assure you that there are Java developers who run circles around whatever your coding skills are. And from your description you don't sound that good at Python either.


Python has proven that a successful dynamic language should have duck-typing, via what called late-binding.

"Python did that?" asks the Smalltalker.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: