I’ve been doing a lot of work, lately, with JavaScript inheritance – namely for my work-in-progress JavaScript book – and in doing so have examined a number of different JavaScript classical-inheritance-simulating techniques. Out of all the ones that I’ve looked at I think my favorites were the implementations employed by base2 and Prototype.
I wanted to go about extracting the soul of these techniques into a simple, re-usable, form that could be easily understood and didn’t have any dependencies. Additionally I wanted the result to be simple and highly usable. Here’s an example of what you can do with it:
var Person = Class.extend({ init: function(isDancing){ this.dancing = isDancing; }, dance: function(){ return this.dancing; } }); var Ninja = Person.extend({ init: function(){ this._super( false ); }, dance: function(){ // Call the inherited version of dance() return this._super(); }, swingSword: function(){ return true; } }); var p = new Person(true); p.dance(); // => true var n = new Ninja(); n.dance(); // => false n.swingSword(); // => true // Should all be true p instanceof Person && p instanceof Class && n instanceof Ninja && n instanceof Person && n instanceof Class
A couple things to note about this implementation:
- Creating a constructor had to be simple (in this case simply providing an init method does the trick).
- In order to create a new ‘class’ you must extend (sub-class) an existing class.
- All of the ‘classes’ inherit from a single ancestor: Class. Therefore if you want to create a brand new class it must be a sub-class of Class.
- And the most challenging one: Access to overridden methods had to be provided (with their context properly set). You can see this with the use of
this._super()
, above, calling the originalinit()
anddance()
methods of thePerson
super-class.
I’m pleased with the result: It helps to enforce the notion of ‘classes’ as a structure, maintains simple inheritance, and allows for the super method calling.
Simple Class Creation and Inheritance
And here’s the implementation (reasonably sized and commented well) – clocking in at around 25 lines. Feedback is welcome and appreciated.
/* Simple JavaScript Inheritance * By John Resig https://johnresig.com/ * MIT Licensed. */ // Inspired by base2 and Prototype (function(){ var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; // The base Class implementation (does nothing) this.Class = function(){}; // Create a new Class that inherits from this class Class.extend = function(prop) { var _super = this.prototype; // Instantiate a base class (but only create the instance, // don't run the init constructor) initializing = true; var prototype = new this(); initializing = false; // Copy the properties over onto the new prototype for (var name in prop) { // Check if we're overwriting an existing function prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn){ return function() { var tmp = this._super; // Add a new ._super() method that is the same method // but on the super-class this._super = _super[name]; // The method only need to be bound temporarily, so we // remove it when we're done executing var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; })(name, prop[name]) : prop[name]; } // The dummy class constructor function Class() { // All construction is actually done in the init method if ( !initializing && this.init ) this.init.apply(this, arguments); } // Populate our constructed prototype object Class.prototype = prototype; // Enforce the constructor to be what we expect Class.prototype.constructor = Class; // And make this class extendable Class.extend = arguments.callee; return Class; }; })();
In my opinion the two trickiest parts are the “initializing/don’t call init” and “create _super method” portions. I want to cover those briefly so that you will have a good understanding of what’s being achieved in this method.
Initialization
In order to simulate inheritance with a function prototype we use the traditional technique of creating an instance of the super-class function and assigning it to the prototype. Without using the above it would look something like this:
function Person(){} function Ninja(){} Ninja.prototype = new Person(); // Allows for instanceof to work: (new Ninja()) instanceof Person
What’s challenging about this, though, is that all we really want is the benefits of ‘instanceof’, not the whole cost of instantiating a Person object and running its constructor. To counteract this we have a variable in our code, initializing
, that is set to true whenever we want to instantiate a class with the sole purpose of using it for a prototype.
Thus when it comes time to actually construct the function we make sure that we’re not in an initialization mode and run the init method accordingly:
if ( !initializing ) this.init.apply(this, arguments);
What’s especially important about this is that the init method could be running all sorts of costly startup code (connecting to a server, creating DOM elements, who knows) so circumventing this ends up working quite well.
Super Method
When you’re doing inheritance, creating a class that inherits functionality from a super-class, a frequent desire is the ability to access a method that you’ve overridden. The final result, in this particular implementation, is a new temporary method (._super
) which is only accessible from within a sub-classes’ method, referencing the super-classes’ associated method.
For example, if you wanted to call a super-classes’ constructor you could do that with this technique.
var Person = Class.extend({ init: function(isDancing){ this.dancing = isDancing; } }); var Ninja = Person.extend({ init: function(){ this._super( false ); } }); var p = new Person(true); p.dancing; // => true var n = new Ninja(); n.dancing; // => false
Implementing this functionality is a multi-step process. To start, note the object literal that we’re using to extend an existing class (such as the one being passed in to Person.extend
) needs to be merged on to the base new Person
instance (the construction of which was described previously). During this merge we do a simple check: Is the property that we’re attempting merge a function and is what we’re replacing also a function? If that’s the case then we need to go about creating a way for our super method to work.
Note that we create an anonymous closure (which returns a function) that will encapsulate the new super-enhanced method. To start we need to be a good citizen and save a reference to the old this._super
(disregarding if it actually exists) and restore it after we’re done. This will help for the case where a variable with the same name already exists (don’t want to accidentally blow it away).
Next we create the new _super
method, which is just a reference to the method that exists on the super-class’ prototype. Thankfully we don’t have to make any additional changes, or re-scoping, here as the context of the function will be set automatically when it’s a property of our object (this
will refer to our instance as opposed to the super-class’).
Finally we call our original method, it does its work (possibly making use of _super
as well) after which we restore _super
to its original state and return from the function.
Now there’s a number of ways in which a similar result, to the above, could be achieved (I’ve seen implementations that have bound the super method to the method itself, accessible from arguments.callee
) but I feel that this technique provides the best mix of usability and simplicity.
I’ll be covering a lot more of the nitty-gritty behind the JavaScript prototype system in my completed work but I just wanted to get this Class implementation out there to get everyone trying it out and playing with it. I think there’s a lot to be said for simplistic code (easier to learn, easier to extend, less to download) so I think this implementation is a good place to start and learn the fundamentals of JavaScript class construction and inheritance.
This topic will be discussed, in depth, in my work-in-progress book: Secrets of the JavaScript Ninja. To be released Fall 2008.
Justin Meyer (March 20, 2008 at 3:14 am)
I really like how you call super. For some reason, I never liked Prototype’s use of $super as an argument, even if the only reason I could ever come up with is that it was hard to get things to pack with it.
Also, the brevity of your code is great. I’m very tempted to steal it for JavaScriptMVC.
John Resig (March 20, 2008 at 3:34 am)
@Justin Meyer: Prototype’s technique is interesting, passing the super method in as the first argument definitely has its pros and cons. Personally, I prefer not to mess with the arguments (adding in extra ones at the beginning, etc.). Well, at least I won’t until we have a mutable arguments object.
One thing that I didn’t do in my solution was to check and see if the super method was really even necessary (beyond just checking to see if a prior function exists). It’s not clear as to how that would be achieved in a reliable manner. I’m amused by how Prototype detects if their technique is required; they convert a function to a string and parse out the arguments, looking to see if the first one has a specific name (as you mentioned ‘$super’). I’m sure this works just fine but to me it just doesn’t sit well (requiring specific variable names seems a little over-bearing).
Thanks for bringing this up, I forgot to mention it.
Dean Edwards (March 20, 2008 at 3:47 am)
That’s pretty much a re-write of my Base.js. I don’t mind. Just saying. :-)
Dean Edwards (March 20, 2008 at 3:57 am)
@John – it is a good idea to test if _super is called. Most methods aren’t overridden, so for the sake of performance it is worth the effort. I use a RegExp in base2. Some platforms don’t allow the decompilation of functions so you must allow for that:
var OVERRIDE = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
if (OVERRIDE.test(prop[name])) { ... }
John Resig (March 20, 2008 at 4:05 am)
@Dean: Hard to say – it’s really a mix of Base.js and Prototype’s base.js. Honestly, I’d say my code is closer to Prototype’s implementation, if there was a comparison occurring.
It’s safe to say that my implementation is woefully simpler than what you provide. No casting, no static properties, no ancestor traversal – to name a few.
The one, obvious, influence is the use of the initialization variable to stop the constructor from running. I liked that a lot, in Base.js.
I was on the fence about checking to see if _super existed, but I guess it’s probably worth that initial overhead. The only platform, that I know of, that fails this would be Opera Mobile. Thanks for the pointer, I’ll roll it in!
Andrea Giammarchi (March 20, 2008 at 4:24 am)
John, if you are writing this in your book, please never forget to set the correct constructor everytime you assign a prototype using an object or a different instance.
var me = new Person;
alert(me instanceof Person); // true
alert(me.constructor === Person); // false
There are a lot of “fake extender” over the net, and even if you called them simple inheritance, I think people should know that everytime you reassign entirely a prototype, constructor will be overwritten as well and you need to explicit specify them, after prototype declaration.
Dean Edwards (March 20, 2008 at 4:28 am)
@John – the inheritance method is the same as Base’s not Prototype. Only the names are similar to Prototype (Class and super).
http://dean.edwards.name/base/Base.js
I like your implementation. It is much simpler than Base. But it is the same inheritance pattern.
ryan (March 20, 2008 at 5:02 am)
Very nice. It is an iteration further and is compact and clean looking. This is one element of jquery that proto and moo etc they have class creation. This is a nice util to add.
Andy Beeching (March 20, 2008 at 5:35 am)
Very useful, if nothing else than for groking some of the more complex sides of JS inheritance (and gotchas as Andrea pointed out above). After just finishing Dustin’s book (Design Patterns) I can actually follow your code and appreciate what it achieves.
Perhaps a useful start for anyone attempting to roll their own library? Thank you for all your articles John, they really are enlightening! :-) Looking forward to the book!
p.s. May I ask what the fnTest xyz is about at the top? I see it being used in the constructor in the method overriding but not sure what decompiling functions mean (what Dean mentioned). Cheers!
Michael Schuerig (March 20, 2008 at 6:11 am)
“JavaScript classical-inheritance-simulating techniques”. Every time I read something like that or see another implementation that implements it, I wonder whether this is a good idea to begin with. I have no good answer, my imagination is as limited to this style of inheritance as is apparently everyone else’s.
Still, I’d really like to see an assessment whether and when class-based inheritance is more useful than prototypal inheritance (delegation).
Lars (March 20, 2008 at 7:16 am)
Is there a way of implementing class-wide private members with this? I usually do this with closures, but I can’t see how that could be done in this scenario.
Toni (March 20, 2008 at 7:36 am)
@Lars
You can make private variables in the init method, for example. You just need to make getters and setters as well, i.e.
init: function() {
// Private var
var _div = $( div );
// Priviledged method accesses private var
this.getDiv = function () { return _div; }
this.setDiv = function (div) { _div = div; }
// other initialization stuff
}
Downside is that the privileged getters and setters can’t be put in the prototype, i.e. they are created for each instance separately. Another issue is the overhead of closures in general.
Fred (March 20, 2008 at 8:04 am)
John-
Definitely looking forward to your book! I’ve been really getting into base2 myself lately, so it’s nice to see that you’re playing with it as well. The superclass method hook is really clever.
// Should all be true
p instanceof Person && p instanceof Class &&
n instanceof Ninja && n instanceof Person && n instanceof Class
My philosophical observation is that, if you’re going to use the name ‘Class’, it should be a factory (like Prototype), not a superclass. It makes sense to say
n instanceof Person
, but notn instanceof Class
. Ideally, you wantn instanceof Person
andPerson instanceof Class
. But JavaScript doesn’t have metaclasses really, so that’s not really possible. Hope that makes some shred of sense.-Fred
Fred (March 20, 2008 at 8:16 am)
Andy-
The xyz test determines whether the browser can inspect the textual body of a function. If it can, you can perform an optimization by only wrapping an overridden method if it actually calls this._super() somewhere in its body. Since it requires an additional closure and function call overhead to support _super, it’s nice to skip that step if it isn’t needed.
-Fred
joel boonstra (March 20, 2008 at 8:35 am)
This is probably an aspect of Javascript I simply don’t understand. I’m wondering why the base Class implementation is created thusly:
this.Class = function Class( ){};
and not just like this:
this.Class = function( ){};
Does specifying the function name maintain the
instanceof
associations? Or are the two snippets equivalent?Andrea Giammarchi (March 20, 2008 at 8:51 am)
@Lars, @Toni, you can create a prototype object inside a closure and have how many private stuff you desire but these will be shared by each instance.
function A(){};
A.prototype = (function(){
var _div = "my searched div";
return {
constructor:A,
getDiv:function(){return _div;},
setDiv:function(div){_div = div;}
};
})();
var a1 = new A, a2 = new A;
a1.setDiv("another div");
alert([a1.getDiv(), a2.getDiv()].join("\n"));
// another div in both case
This does not happen if you choose “private methods” and call them using call or apply, these will be shared as well but these will depend on the chosen instance.
function A(){
this._div = "my searched div";
};
A.prototype = (function(){
function _setDiv(_div){
this._div = _div;
};
return {
constructor:A,
getDiv:function(){return this._div;},
setDiv:function(_div){_setDiv.call(this, _div);}
};
})();
var a1 = new A, a2 = new A;
a1.setDiv("another div");
alert([a1.getDiv(), a2.getDiv()].join("\n"));
// another div, my searched div
Of course if function does not work with instance methods or properties, you can call them directly.
// inside prototype closure ...
function _sum(x, y){return x + y;};
In this case you could share a function, instead of a method, so you do not need injected scope, just use _sum(1,2) to get 3.
These are little tricks that probably not everyone knew and privileged methods, as you can see, are not necessary (read: my 5 cents)
Regards
Andy Beeching (March 20, 2008 at 10:00 am)
Fred,
Thank you for the explanation, haven’t seen that trick before (inspecting a functions’ contents for certain strings – would this be an old school eval use as well?), but the optimisation certainly makes sense in terms of skipping redundant checks. Cheers!
// Andy
Tom (March 20, 2008 at 10:22 am)
It doesn’t make since for a ninja instance object to be an instanceof Class. Maybe the Ninja _class_ object could be an instanceof something called called Class. You might do better to call the inheritance root “Any” or something like that. (Obviously not Object since that’s already taken.)
Tom (March 20, 2008 at 10:30 am)
And to Lars and related matters, I still like the “new function() {…}” model for executing code blocks. You get a “this” object to which you can assign values you want exported, and other vars are class-wide privates, such as (going back to John’s Class instead of my recommended Any to cover fewer concepts at once):
var Counter = Class.extend(new function() {
var count = 0;
this.up = function() {
count++;
};
this.down = function() {
count--;
};
this.current = function() {
return count;
};
});
var counter = new Counter();
counter.up();
counter.up();
counter.down();
counter.current(); // returns 1
I haven’t actually tested the code with John’s class implementation, but it should work (modulo any typos, etc.). A bit more typing than the object literals but still straightforward and works well.
Andrea Giammarchi (March 20, 2008 at 10:56 am)
@Tom, your inline instance has exactly the same behavior of my example with object in scope. I do not know which way is better, but count variable will be shared for every instance and this should be a problem.
var c1 = new Counter(),
c2 = new counter();
c2.up();
c1.current(); // returns 1
Be careful with shared variables in private scope (but probably you knew it, didn’t you?)
timothy (March 20, 2008 at 11:14 am)
This is nice. John, you’re not thinking of putting it in jQuery, are you? Seems like you decided in the past that jQuery doesn’t need it.
I agree that Base and Base2 are a nice choice to use with jQuery. Also, Low Pro for jQuery is cool, too. Dan Webb (Low Pro’s author) is one of the members of the core Prototype team now, and he was the tech editor (I believe) of John Resig’s JavaScript book.
As for inheritance, I find that most often I just don’t use it. Even in JavaScript programs that are thousands of lines. Maybe it’s the old-school C programmer in me talking, but now that I’m used to it, JavaScript’s object system seems perfect to me. I love it. It’s like a struct that I can add stuff to at any time. I’m beginning to lose interest in classical OO.
Ted Henry (March 20, 2008 at 11:59 am)
“super” is a FutureReservedWord in ECMAScript v3.
What happens if you have classes A > B > C and A and C both have a method “foo” and you want to call super in “C.foo”? Do you need to create a dummy B.foo to make it happen?
Tom (March 20, 2008 at 11:59 am)
@Andrea, thanks for the clarification. I’m not used to even trying class inheritance in JavaScript, so I wan’t thinking about the shared prototype. (Silly me.) As one off objects (which I usually use), my style works fine. I’ll have to think about the issues more from the shared perspective.
Andrea Giammarchi (March 20, 2008 at 12:20 pm)
@Ted … that’s probably why no one used super :-)
John used _super, I used Super, and about your question, if you do not assign a method, it’s inherited from the parent one, so B will inherit the method and usage of super will call the first one that has been declared.
var A = Class.extend({
value:"A",
doStuff:function(){
return this.value;
}
});
var B = A.extend({});
var C = B.extend({
doStuff:function(){
return this._super();
}
});
var c = new C;
alert(c.doStuff());
@Tom, you’re welcome, and if I can add something … the concept is simple: prototype is an instanceof whatever (natively of an Object). If you assign an instance, or an object, as constructor prototype, every “new constructor” will share its methods. If those are created inside a scope, every instance will share that scope.
function A(){
var scope = "private";
this.getScope = function(){return scope;};
this.setScope = function(s){scope = s};
};
var a1 = new A, a2 = new A;
a2.setScope("a2");
a1.getScope() === a2.getScope(); // false
// but if you use a1 as prototype
function B(){};
B.prototype = a1;
Every new B will use a1 methods and a1 methods have been declared once in a scope that will be shared for every B instance. That’s why the private var scope will the same for every B instance, while a2 was created in a different scope (a new one).
I think it’s more difficult to explain than to understand, at least I hope so :D
Fred (March 20, 2008 at 12:38 pm)
Tom-
That was the gist of my observation as well. In base2 the “base” object is called Base. :-)
-Fred
Fred (March 20, 2008 at 12:40 pm)
Ted-
No, you don’t need a dummy B#foo because A#foo is in C’s prototype chain. When you call this._super() from C#foo, it will look up B.prototype.foo, which is A#foo.
-Fred
Fred (March 20, 2008 at 12:43 pm)
Joel-
Specifying the function name doesn’t alter the behavior in any way, it just gives the function object a name, if you happen to need one.
(function () {}).name == ""
(function asdf() {}).name == "asdf"
-Fred
Justin Meyer (March 20, 2008 at 12:49 pm)
John, is there a way for your class to know its name? This is so developers can do something like:
var n = new Ninja();
n.klass -> 'Ninja'
This opens a lot of doors for using naming conventions for inherited classes. The only way I’ve added this functionality is including the name of the class you want created:
Class.extend('Person', {...});
Ninja.extend('Ninja', {...});
But that seems lame.
Tom (March 20, 2008 at 12:53 pm)
@Andrea, I understand the prototype idea. I just usually don’t use the feature, so I wasn’t thinking through the issues properly. Still, thanks for the quick summary. It can still be helpful for me or others.
Tom (March 20, 2008 at 12:55 pm)
@Fred, thanks for the pointer back to your prior comment. Skimming so many comments gets tricky. (And here I am adding to the weight.)
Samuel Lebeau (March 20, 2008 at 1:31 pm)
The “Class” name sounds strange to me : as “Person” is the class of people, “Class” is meant to be the class of classes. It seems that such a class cannot be created in Javascript because a constructor cannot construct other constructors as far as I know.
I would suggest “BaseObject”, but Dean would kill me !
Just kidding, very nice and clean however!
Fred (March 20, 2008 at 1:52 pm)
Justin-
I agree that it would be enormously helpful for an object to be able to know the name of its class.
Passing the class’s name into the class-creation function may seem lame, but this is what you have to do for base2 Packages, and it makes working with packages really nice. It wouldn’t bother me at all to explicitly name my classes when they’re created. I’d just get used to it, or add it to my code templates, or whatever. :-)
-Fred
John Resig (March 20, 2008 at 1:54 pm)
@Andrea: Good call on setting the constructor. I’ve adjusted the example with the adapted code.
@ryan, timothy: I don’t plan on adding this to jQuery. I feel as if jQuery provides a solid alternative style of coding that doesn’t make this technique particularly necessary. That’s not to say that it isn’t useful – I just think that the intersection between jQuery and classical inheritance is quite small.
@Andy: That’s pretty much the goal – my book is going to be designed to give you a solid understanding of the pieces of a library. Obviously you can use this knowledge in two ways: Get a better understanding of existing libraries that you use or write a library of your own.
@Michael: The usefulness of one method vs. another is debatable. If you’re just doing simple stuff then just use straight prototypal inheritance – there’s no reason to have any extra overhead. However when it comes time to stuff like super methods you’ll need some construct to make it happen.
@Fred: It sounds like this may be more of a terminology issue, than anything else (calling it Class implies that it was used to create a new class). I’ll consider revising it, but as it stands, I’m not heart-broken about it.
@joel: Good point concerning function Class(){}. That extra Class was unnecessary and has been removed. Since the ‘Class’ name is only visible within the scope of the function itself, and since there’s nothing within the function, there’s no point in having that extra code.
@Justin: I don’t think there’s a reasonable way to actually assign a name to the classes, which is unfortunate.
Fred (March 20, 2008 at 2:48 pm)
John-
Something I just thought of, for the sake of exception safety: if the chained method throws an exception, it will leave the _super property set to a bad value. Probably not a big deal in ordinary cases, but it’s a trivial fix:
var tmp = this._super;
this._super = super[name];
try {
return fn.apply(this, arguments);
} finally {
this._super = tmp;
}
What do you think? Is the try/finally overhead worth the benefit?
-Fred
Ted Henry (March 20, 2008 at 3:08 pm)
“var super” is in the code and “super” is a FutureReservedWord.
MillsJROSS (March 20, 2008 at 3:22 pm)
It’s all about time analysis when it comes to inheritance abstractions like this. How much speed am I losing with this abstraction? The difference might be negligible, or it might not. I might be willing to spare some time for clarity in my code, but I’m not sure how much more clear this makes my code. I tried implementing a “better” way to capture the idea of inheritance and class based code when I was learning JS. I turned it into something easy to understand, and easy to use…but along the way I grokked how inheritance worked in JS, and haven’t looked back.
That said, I do think it’s good to flex your programming skills and try to improve something you might think is lacking, and I’ve definitely learned a little by reading your code. Thanks for this.
John Resig (March 20, 2008 at 4:24 pm)
@Fred: Good point about the try/finally. I’ll probably roll that fix in, soon.
@Ted Henry: Good catch. I’ve renamed the variable to _super.
Mike Purvis (March 21, 2008 at 10:01 am)
Hmm… I tried to write a comment yesterday, but it seems to have been swallowed up by the internets. Anyhow, two things—
Firstly, how does this “classical inheritance” compare with what goes on inside jQuery? jQuery really just has the one main class with instances all over, right? So is that in some way better, or just a different approach? Are there tasks that work better with one or the other?
Secondly, how does this compare to the new keywords being proposed for JavaScript 2? If an approach like this is adopted, will it be possible down the road to amend it to use the native methods in browsers that support them?
Fred (March 21, 2008 at 11:12 am)
Mike-
1) jQuery doesn’t really have any kind of “object model,” so it wouldn’t make much sense for it to be composed of classes. A domain model on the other hand might benefit from the conceptual “fit.” I always answer this question with, “Whatever makes your code easier to write and easier to understand.” :-)
2) It might be possible to make some kind of API that does this metaprogramming magic in JS1, and forwards onto native methods in JS2, but my hunch is that JS1 class schemes like this will have to be ported to JS2. It should mostly involve class construction though; the actual invoking of methods on objects shouldn’t need to change much.
-Fred
John T (March 21, 2008 at 11:56 am)
John,
How does your method support abstract methods called in the super constructor? My experience with Crockford’s inheritance hack (Ninja.prototype = new Person()) is that this fails to support my (arguably special) case.
The technique I use sacrifices the “instanceof” operator for this case. This tradeoff is fine with me because the operator, in my opinion, is not very useful compared to functional abstract methods.
var Person = function() {
this.abstractMethod();
}
var Ninja = function() {
Person.call(this); // call the super constructor
}.mixin(Person)
Ninja.prototype.abstractMethod = function() {}
Where Function.prototype.mixin is a simple mixin extension. You could write this in jQuery as:
Function.prototype.mixin = function(p)
{
return $.extend(this.prototype, p.prototype);
}
Another benefit of this method is you get multiple inheritance for free.
Dean Edwards (March 21, 2008 at 4:56 pm)
@John – I just remembered that using “prototype” as a variable name causes a bug in some older versions of Firefox.
Also, wrapping try/catch around the _super method results in much slower code. I took it out of base2 and no one has complained (so far).
Andrea Giammarchi (March 22, 2008 at 2:27 pm)
@John T, what you can do with objects, you can do with instances … so, as you know, since prototype is always an instance, and you can add or overwrite whatever you want in that instance, which is this case where you have to sacrifice instanceof operator?
var Ninja = function() {
Person.call(this);
};
(function(){
var i = function(){};
i.prototype = Person.prototype;
Ninja.prototype = new i;
Ninja.prototype.constructor = Ninja;
})();
var n = new Ninja;
(n instanceof Person) // true
Miodrag Kekic (March 24, 2008 at 10:22 am)
A minor correction:
// Enforce the constructor to be what we expect
Class.constructor = Class;
should be
// Enforce the constructor to be what we expect
prototype.constructor = Class;
wakacji czas (March 24, 2008 at 4:24 pm)
java is very interesting language, Yesterday i bought the book about script of java and there is a article where autor wrote about java whichj is use everywhere, flash, html :)
Remco (March 26, 2008 at 3:30 am)
What on earth is this?
/xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/
A ternary expression which appears to be returning a pattern but returns a function instead? Passing a dummy function to Regex#test? Only the first, after the question mark, expression being evaluated? Where can I find more information on this magic?
Remco (March 26, 2008 at 6:05 am)
Aaah, now I understand;
var canDecompile = /xyz/.test((function(){xyz}).toString())
var fnTest = canDecompile ? /\b_super\b/ : /.*/
and you depend on a Firefox extension as Dean noted..
Jonas Sicking (April 17, 2008 at 3:20 pm)
Alex Frize was developing a system like this when he was developing Zap (VoIP client based on gecko).
Nguyen (April 18, 2008 at 7:03 pm)
Help me!
var A = Class.extend({
a: function(){return ‘a’},
b: function(){return ‘b’}
});
var B = A.extend({
a: function(){return ‘B.’ + this.SUPER()},
b: function(){ /* how to call A.a() */}
});
How can I call the method a() in parent class? Take a note that a() is overridden in B class.
Nguyen (April 18, 2008 at 7:07 pm)
Sorry, I help a mistake in the previous posting. Here is my correct one. Help me!
var A = Class.extend({
a: function(){return ‘a’},
b: function(){return ‘b’}
});
var B = A.extend({
a: function(){return ‘B.’ + this._super()},
b: function(){ /* how to call A.a() */}
});
How can I call the method a() in parent class? Take a note that a() is overridden in B class.
Nguyen (April 18, 2008 at 7:07 pm)
Sorry, I help a mistake in the previous posting. Here is my correct one. Help me!
var A = Class.extend({
a: function(){return ‘a’},
b: function(){return ‘b’}
});
var B = A.extend({
a: function(){return ‘B.’ + this._super()},
b: function(){ /* how to call A.a() */}
});
How can I call the method a() in parent class? Take a note that a() is overridden in B class.
M (May 12, 2008 at 4:06 am)
Hi,
I have tried an different techniques related to JavaScript inheritance subject.
The code and the explanation are to long to post them here so if anyone is interested to take a look over it you can find it at http://www.dotnetcaffe.net under JavaScript category. Fell free to criticize the code in any way you want…just don’t flame :).
jeff (May 18, 2008 at 9:16 am)
Can you extend the builtin objects with this? such as String or Error?
The following didn’t work for me:
var MyException = Class.extend(Error.prototype);
var me = new MyException();
alert(me instanceof Error); // returns false
jeff (May 18, 2008 at 12:50 pm)
I overcame the above using two methods which i’m not too thrilled about anyway:
1. Redefine builtins:
Error = Class.extend(Error.prototype);
var MyError = Error.extend({});
var me = new MyError();
alert(me instanceof Error); // returns true
Looks a little scary. Not sure of the ramifications.
2. Alter the inheritence code:
Here I add another options parameter called base and i use it to create the prototype for what Class.extend returns:
Class.extend = function(prop, base) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = base? base : new this();
and then i call the modified Class code this way:
var MyError = Class.extend({}, Error.prototype);
var me = new MyError();
var YourError = MyError.extend({});
var ye = new YourError();
alert(me instanceof Error); // returns true
alert(ye instanceof Error); // returns true
alert(ye instanceof MyError); // returns true
Dave Schoonover (May 20, 2008 at 5:39 pm)
Jeff, you can extend native types like this:
var SillyError = Class.extend.call( Error, {
init: function(msg){
this.message = msg;
}
});
var e = new SillyError("giggle!");
e instanceof SillyError; // true
e instanceof Error; // true
The trick is that the prototype of the child class is created with a call to “new this();”. If you want to extend any class which was not created with Class.extend, you’ll need to invoke extend with a different object context: Class.extend.call( ParentClass, { … });
Dave Schoonover (May 20, 2008 at 5:52 pm)
Additionally, if perhaps you want to extend native objects frequently and you’re not afraid of adding methods to the native object constructors (it is NOT required to add to their prototypes!), you can do this:
// Attach to the Array constructor
// NOT Array.prototype
Array.extend = Class.extend;
var List = Array.extend({ init: function(zero){ this[0] = zero; } });
var feh_list = new List("feh!");
feh_list instanceof List; // true
feh_list instanceof Array; // true
alert( feh_list[0] ); // alerts "feh!"
jeff (May 21, 2008 at 4:00 pm)
Dave, good work. I’m still having problems with this. Could you change your example and create something that extends String. This subclass should be able to delegate construction of itself to String. It must also have all String capabilities such as substring() and other methods. In your array example i’m not sure if List is a full array or just because of this[0] statement. If you prefer you could just continue this by emailing me: [email protected]. thanks
Dave Schoonover (May 26, 2008 at 5:46 pm)
Yeah, you’re going to have trouble extending any of the built-in objects which are also non-object primitives: String, Number, and Boolean. Probably Function as well. This is because the methods toString, valueOf, and toSource are native code methods, and incompatible with whatever your subtype happens to be.
You can, however, clobber them when you extend:
var S = String.extend({
init: function(s){
function toPrimitive(){ return String(s) }
this.valueOf = toPrimitive;
this.toString = toPrimitive;
this.toSource = toPrimitive;
}
});
var s = new S("feh");
alert( s + " whaa!" ); // alerts "feh whaa!"
alert( "woo " + s ); // alerts "woo feh"
alert( s.substring(1) ); // alerts "eh"
There might be other native code methods. If you encounter anything else that doesn’t work, try this same trick on the complaining method.
Dave Schoonover (May 26, 2008 at 6:08 pm)
@Nguyen:
If you want to call A.a in B (which has A as a parent)… why not call it directly?
var A = Class.extend({
a: function(){ return 'a'; },
b: function(){ return 'b'; }
});
var B = A.extend({
a: function(){ return 'B.' + this._super(); },
b: function(){ return 'B.' + A.prototype.a.call(this); }
});
I actually prefer this to the _super convention John has added, even though I admit it is novel. In my case, I write all my instance methods (those attached to the prototype) as static class methods (attached to the class), and then wrap them with the “methodize” idiom. (You can find an example of methodize in MochiKit, Prototype, and other libraries.)
Eric Miller (May 30, 2008 at 10:47 am)
Thanks for this. I’ve been using it a bit and it’s super. One question: I’d like to capture the “class name” in a property of each instance so that it can be stored when the instance’s data is serialized to json and then used to rehydrate objects later. I’ve not found a nice way to do that using this scheme, other than passing the name in by hand, eg:
B = A.extend({ ... }, "B");
Since instanceof works, it seems like there *should* be a way to get the class name during construction but I have yet to find it. Any suggestions? Thanks in advance!
Luke (June 21, 2008 at 5:08 pm)
Been playing with this, but it seems to break if a class has multiple subclasses… That is,
var a = Class.extend({});
var b = Class.extend({});
var c = new a();
c instanceof a => False
c instanceof b => True
Not only is instanceof wrong, but ‘c’ will act as a ‘b’ too.
Also, assuming this problem gets fixed… what license is this available under? (I’m hoping for BSD-like)
Luke (June 21, 2008 at 5:12 pm)
Correction: instanceof returns True for both cases, but it still *acts* like ‘b’. This problem doesn’t occur with SpiderMonkey, but does with SEE.