The world's smallest and fastest classical JavaScript inheritance pattern
github.com
github.com
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.
exports.inherits = function(ctor, superCtor) {
ctor.super_ = superCtor;
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};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...
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...
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;
}
}); 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. 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 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.
"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.
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.
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.
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).
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.
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.
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.
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);
The latter part of this line needs a bit of documentation.
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.
>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.
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.
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.
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.
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.
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.
Disclaimer : I'm not actually a big fan of javascript.
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.
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 did that?" asks the Smalltalker.