A Base Class for JavaScript Inheritance
NB. I’ve amended the code samples below to reflect recent changes in this class.
I’m an OO programmer at heart and JavaScript supports prototype based inheritance. Unfortunatley this leads to verbose class definitions:
function Animal(name) {}; Animal.prototype.eat = function() {}; Animal.prototype.say = function(message) {};
I want a nice base class for JavaScript OO:
- I want to easily create classes without the
MyClass.prototype
cruft - I want method overriding with intuitive access to the overridden method (like Java’s
super
) - I want to avoid calling a class’ constructor function during the prototyping phase
- I want to easily add static (class) properties and methods
- I want to achieve the above without resorting to global functions to build prototype chains
- I want to achieve the above without affecting
Object.prototype
The Base Class
I’ve created a JavaScript class (Base.js) that I hope eases the pain of JavaScript OO. It’s a simple class and extends the Object
object by adding two instance methods and one class method.
The Base
class defines two instance methods:
extend
Call this method to extend an object with another interface:
var object = new Base; object.extend({ value: "some data", method: function() { alert("Hello World!"); } }); object.method(); // ==> Hello World!
base
If a method has been overridden then the base
method provides access to the overridden method:
var object = new Base; object.method = function() { alert("Hello World!"); }; object.extend({ method: function() { // call the "super" method this.base(); // add some code alert("Hello again!"); } }); object.method(); // ==> Hello World! // ==> Hello again!
You can also call the base
method from within a constructor function.
Creating Classes
Classes are created by calling the extend
method on the Base
class:
var Animal = Base.extend({ constructor: function(name) { this.name = name; }, name: "", eat: function() { this.say("Yum!"); }, say: function(message) { alert(this.name + ": " + message); } });
All classes created in this manner will inherit the extend
method so we can easily subclass the Animal
class:
var Cat = Animal.extend({ eat: function(food) { if (food instanceof Mouse) this.base(); else this.say("Yuk! I only eat mice."); } }); var Mouse = Animal.extend();
Class Properties and Methods
A second parameter passed to the extend
method of a class defines the class interface:
var Circle = Shape.extend({ // instance interface constructor: function(x, y, radius) { this.base(x, y); this.radius = radius; }, radius: 0, getCircumference: function() { return 2 * Circle.PI * this.radius; } }, { // class interface PI: 3.14 });
Note the use of the base
method in the constructor. This ensures that the Shape
constructor is also called. Some other things to note:
- If you define a class method (not an instance method) called
init
it will be automatically called when the class is created - Constructor functions are never called during the prototyping phase (subclassing)
Classes With Private Data
Some developers prefer to create classes where methods access private data:
function Circle(radius) { this.getCircumference = function() { return 2 * Math.PI * radius; }; };
You can achieve the same result using the Base class:
var Circle = Shape.extend({ constructor: function(radius) { this.extend({ getCircumference: function() { return 2 * Math.PI * radius; } }); } });
The code is slightly more verbose in this case but you get access to the base
method which I find incredibly useful.
Single Instances
I changed my mind a lot about this but finally decided to allow the creation of single instance classes by defining a null
constructor:
var Math = Base.extend({ constructor: null, PI: 3.14, sqr: function(number) { return number * number; } });
Conclusion
This supersedes my old OO framework which was not as cross-browser as I would have liked (and had other shortcomings).
I have a development area where I’m developing bits and pieces based on this engine. Most of this is work in progress but illustrates the flexibility of this technique.
You can grab the Base
class here: Base.js.
Update. I’ve posted a follow-up to this post with some improvements suggested in the comments below.
Comments (218)
Leave a comment
Comment: #1
In the Circle examples you have declared a method “area” which is “perimeter” instead Area is Math.PI *this.radius ^ 2
Anyway it’s really a simple and effective system to simplify class declaration and inheritance.
Comment: #2
The JS inheritance class that I use is from Doug Crockford. It’s a nice simple class inheritance base class that just modifies the main Function class. It supports calling parent methods and doing multiple inheritence (which thus far I have never found a need for)
The only change I had to make from his code to make it fully cross-browser (opera had some issues) was to change his declaration of ‘uber’ in the ‘inherits’ method to NOT specify “function uber(name)” but instead use “function (name)” (anonymous function).
Comment: #3
@Franco – Oops! Fixed now. Thanks. Maths was never my strong point (actually it was but I’ve now forgotten everything )
Comment: #4
What benefits do you think this has over the Object.extend() and Class.create() features of Prototype?
Comment: #5
not work in firefox
“prototype.call is not a function”
Comment: #6
@modi – What doesn’t work?
Comment: #7
I got message “prototype.call is not a function” in Javascript Console when i try to Creating Class
Comment: #8
@modi – you are going to have to show me some code. I’ve created dozens of classes in Firefox and have not seen the error you mention.
Comment: #9
I was also about to ask the same thing as Jonathan…
Comment: #10
@Mislav/Jonathon,
There is nothing wrong with Prototype’s method. I prefer mine for the following reasons:
inherit
methodIt’s like comparing
x=x+1
withx++
. A slight improvement but an improvement nonetheless.Read this for a better explanation: www.sitepoint.com/blogs/2006/01/17/javascript-inheritance/.
Comment: #11
As for readability, I see hardly a difference. As for the inherit method, this is really a blank spot in prototype.js
Unless you apply the patch from dev.rubyonrails.org/ticket/4060
Comment: #12
@Martin – This is not designed to compete with Prototype. If people are comfortable with that library then they should continue using it. I wrote this for my own needs and I thought I’d share it.
Comment: #13
Dean, I think when you’re writing “overload”, you mean “override”.
Two method declarations of the same name but different parameter lists are said to be “overloaded”. This isn’t possible in JS (unless you count using the arguments object and inspecting the passed argument types).
A method reimplemented which was defined in a superclass is said to be “overridden”.
Right?
Comment: #14
Ah, that really makes a difference. I particulary like Ben’s patch; thanks for the link, Martin.
Dean: about singletons… The example provided isn’t exactly a singleton pattern, just a class that cannot be instantiated with some class constants and methods. I would call that a static class (or something like that), not a singleton.
And why did you choose to call the method
inherit
? Wouldn’tsup
orsuper
orparent
be a better name?Apart from that I like the work you’ve done very much, especially the clear examples.
Comment: #15
Dean, I never thought you want to compete with prototype.js, and I am certainly not a “XY already has this, no need for this” fan-boy. Sorry if this is what it looked like.
More choices are good, and one can always learn from other code, especially if it is yours, and adapt ideas. So thank you for sharing some of your work once again.
Comment: #16
[...] art man, and wasn’t happy with any Base classes out there in various libraries so he wrote his own Base.js. His goals: I want to easily create classes without the MyClass.prototype [...]
Comment: #17
Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.7.12) Gecko/20050919 Firefox/1.0.7
Comment: #18
Nice work.
In the private data example, did you mean to use just ‘radius’ in the formula instead of ‘this.radius’, as in…
Comment: #19
@modi – Fixed now. Thanks for letting me know. I had to rename a variable from
prototype
to_prototype
in a couple of functions. Presumably this is a bug that has been fixed in later versions of Firefox.Comment: #20
@Jeremy/Mislav – I’ve changed the text of the post. I now use the expressions “override” and “single instance”. Thanks for correcting my poor use of computing terminology.
@Steve – Fixed now. Thank you.
Comment: #21
Just a detail… I think inherit should be called something different. (It’s a semantic thing only, nothing wrong with the code in itself)
When you call
baseclass.extend
it is correctly a verb and your action is that expressed by the verb: extending your base class. But if I understood this correctly, when you callthis.inherit
you’re not really inheriting something, but actually calling the inherited method. The difference is subtle, I know, but it somehow doesn’t seem completely right.I might suggest
inherited
instead, if you want to keep the inherit root, or something along the lines of what Mislav suggested above.Comment: #22
What do others think of the name
inherit
? Any other ideas?ancestor
? I don’t like the other suggestions much (inherited
,parent
,sup
) andsuper
is already a reserved word.Comment: #23
I have a couple of questions:
Whats the use of “…If you define a class method (not an instance method!) called init then will it …”, I thought thats what the constructor method was for.
Also this may be a newb question but when you stated that the ‘… second parameter passed to the extend method of a class defines the class interface’ in your example does is the PI property set the to Shape class?
- Thank you for all your contributions
Comment: #24
BTW – I like ‘ancestor’ it gives me a better since of what its doing
Comment: #25
Douglas Crockford proposed a long time ago the word
uber
. I’m not really sure I like it, but there it is anyway.I do like
ancestor
.parent
is ok too.Comment: #26
@John – You can pass two parameters to
class.extend
. These parameters should be object literals. The first parameter defines the instance interface of the class. The second defines the class interface itself. You can perform class initialisation (rare but sometimes needed) by defining aninit
method on the class interface. This method will be called immediately the class is created. This is very different to the constructor function.Below is a more detailed example. Class initialisation is required here because we need to close all open files when the document is unloaded. So we define an
init
method which adds an event handler:Does that makes sense? If you are not sure then ignore class initialisation. Like I said, it is rarely need.
Comment: #27
As for the name: inherited() looks like a test that returns a bool. Aside: I like that ruby allows questionmarks in names, like “SomeObject.inherited?”
“parent()” is nice, but might be too often used already in objects (to express the relationship between two objects like in the DOM).
Nice example in #26. Shows of the lib quite well. The class / instance interface distinction is very neat.
Comment: #28
So the init is similer to the
var foo = Class.create(); Object.extend(foo,{
initialize: function(){ … } });
of Prototype?
Comment: #29
foo = Base.extend({
constructor: function(){
this.bar = 3;
},
bar = 3 },
{
init: function(){
this.bar = 3;
},
bar = 3
});
In this example I have set bar in alot of places. In both the instance and class interface. Also in the construcor and init methods. They all do the same thing right?
If I only did it in the constructor method would alert(foo.prototype.bar); // still show 3
So I guess I dont see the need for an instance interface and a class interface, don’t they both do the exact same thing?
Comment: #30
BTW thank you for taking your time to help and show examples
Comment: #31
@John – You’ve missed the point slightly. Have another look at the example I posted.
Comment: #32
More suggestions instead of “inherited”:
And all those suggesions prepended w/ “call”, so “callBase”.
Comment: #33
Err, any chance of this being made MIT, BSD, AFL, or similar? LGPL? Really?
Comment: #34
Hey, since this is a “refactored” version of common are we going to see an IDE? That was a fun project I thought (more so since I’ve started playing with smalltalk)
Anyway, Base is nice. I do agree with the folks that think the “inherit” method seems oddly named. I say forget all that tired language of “super”, “parent” or “inherit” and go with something new. I like “basis” as it goes well with the name Base. Mmm… “basis”
Comment: #35
So I just answered my own question about the IDE. I looked through your development area, and was stoked to see all that. Nice work! How long have you been at this? This has way more written on top of it then common ever did.
Comment: #36
@Jeremy – I’ll look at changing the license for this.
@Mitchell – Yes, common was fun and I managed to write at least one complex application using it. Base is more cross-browser. The IDE is unfinished. The IDE is useful for browsing object hierarchies but less useful for actually writing code. Until someone writes a decent text editor that is…
And you are right. The IDE is based on Smalltalk.
Comment: #37
Very nice. Not sure if I will use it since I’ve already written so much of my code using prototype but this is the cleanist implementation of class based OO I’ve yet seen.
I was a bit unclear about the point of the _static argument (i.e. second argument in Base.extend( , ) ). It seems to add methods just to the class but not to any instance variables but it also doesn’t seem to be inherited. Is this the intended behavior or am I just missing something in the code? I guess I’m just a bit confused as to what sort of OO model you have in mind with the static stuff.
One thought for future improvement. It seems possible that if you use object.extend inside an event handler or more likely the return from an XHR the global flag Base._prototyping could cause some problems. It seems possible to instead avoid the problem of calling the initializer when you new this by instead copying this to a temp variable renaming the constructoring calling new and then returning the constructor. However, maybe I am missing something.
Comment: #38
@logicnazi – It is my intention that static methods and properties are not inherited. I’ve provided this mechanism mainly for the provision of class constants. Look at JavaScript’s
Number
class as an example of what I mean:phrogz.net/ObjJob/object.asp?id=172
As to your other point. The
Base._prototyping
flag is only set when creating a class. It is not set when usingobject.extend
.Comment: #39
Hmm… feels a lot like the Dojo toolkit’s ‘dojo.lang.extend’, except they’re missing a lot of the extended functionality you’re providing.
Might be worth a look, though, as you seem to be on the same track: manual.dojotoolkit.org/lang.html
Comment: #40
Ohh right I see about the _prototyping flag. I retract my point. It would be kinda wastefull to add all that code for the crazy situation where people are defining new classes in event handlers.
Okay, now I understand what you meant by static. I was just a bit confused by the terminology.
Thanks alot for your response not to mention the library.
Comment: #41
If I try to pass a function in a class, I lose the properties of that class. Any idea why?
Comment: #42
Mark – this is what should happen. When you call
myMethod
, thethis
value is pointing to the global (window
) object. Because there is nowindow.val
defined the function returnsundefined
.Comment: #43
@Mark:
Javascript “methods” are just functions hanging off an object– the “this” keyword isn’t bound to the object if you don’t scope it with the “.” operator.
Check out Boodman’s “hitch” function (which he called “close” in that post) — it makes the function do what you want
(from youngpup.net//2005/0525024806/comments#c1444 )
You’d change this line:
var myMethod = obj.method;
to this:
var myMethod = hitch(obj,’method’);
Comment: #44
Hello.
Great tool Dean, I like it and I will start using it. Thanks.
Just two comments:
Thanks for your attention.
Comment: #45
I have already converted IE7, packer and my syntax highlighter to Base. I haven’t released any of this code yet. Some of the classes are here:
dean.edwards.name/base/text/src/
Comment: #46
Dean, I saw that you mentioned that you didn’t want to mess with the Object.prototype. Have you considered muffing with the Function.prototype instead? Like…something like this:
This typically shows up in Crockford’s toolkit and I’ve found it to be pretty nice.
Comment: #47
[...] ebdesign imagereplacement) gotAPI.com :: API lookup service (tags: reference html css) A Base Class for JavaScript Inheritance (tags: javascript oop) [...]
Comment: #48
@Dustin – Yeah, this was the way I wrote it originally. But adding an override capability meant that I had to add the
inherit
mechanism too. It felt too much like I was extending the language rather than just adding a new class.Comment: #49
Dean, how would you use your library for implementing a class that contains both static and non-static members and properties in a class?
Comment: #50
@Ympostor – An example of a class with both a class and instance interface is shown in Comment #26.
Comment: #51
neat! the inherit() method is really useful but has been missing badly in other libs i have come across. don’t really like the name though. i vote for base() or super().
Comment: #52
Nobody likes
inherit
except me :-(.super
is already a reserved word. These are the alternatives that I like:I’m still undecided…
Comment: #53
What about polymorphism?
I implemented a fully supported but complicated Object Oriented JavaScript library: j2s.sourceforge.net/j2sclazz/ And the article: j2s.sourceforge.net/articles/oop-in-js-by-j2s.html
It seems it also fits all of your requirements of OO.
Comment: #54