This is another trick that I’ve been using for a while to simplify Class-style instantiation of a function in JavaScript. Take a look at the following code, for example:
function User(first, last){ this.name = first + " " + last; } var user = new User("John", "Resig");
Pretty straight-forward. Now let’s look at a couple quirks of Class-style instantiation that can cause issues, but can be easily remedied.
First is an issue that new JavaScript programmers will encounter. On a quick observation of the above code it isn’t immediately obvious that the function is actually something that is meant to be instantiated with the ‘new’ operator (Unless, of course, you understood that the use of ‘this’ within the function implied that it was meant to be instantiated). Thus, a new user would try to call the function without the operator, causing severely unexpected results (e.g. ‘user’ would be undefined).
Secondly, if a Class-style function is meant to be instantiated, and isn’t, it can in turn end up polluting the current scope (frequently the global namespace), causing even more unexpected results. For example, observe the following code:
var name = "Resig"; var user = User("John", name); // user is undefined // AND name is no longer "Resig" if ( name == "John Resig" ) { // Oops! }
This can result in a debugging nightmare. The developer may try to interact the name variable again (being unaware of the error that occurred from mis-using the User function) and be forced to dance down the horrible non-deterministic wormhole that’s presented to them (Why is the value of their variables changing underneath their feet?).
Finally, a tangible advantage. Instantiating a function with a bunch of prototype properties is very, very, fast. It completely blows the Module pattern, and similar, out of the water. Thus, if you have a frequently-accessed function (returning an object) that you want people to interact with, then it’s to your advantage to have the object properties be in the prototype chain and instantiate it. Here it is, in code:
// Very fast function User(){} User.prototype = { /* Lots of properties ... */ }; // Very slow function User(){ return { /* Lots of properties */ }; }
Now, with that in mind, let’s also examine constructing a simple function API. For example, the $ function from jQuery. There’s no way that the users are going to want to do new $("div")
every time they interact with that method; however, we want to be able to take advantage of the speedy benefits of the object prototype. Therefore, we need a solution to solve this.
Now, without further hassle, here is the solution to all of the above problems, in a simple snippet:
function User(first, last){ if ( this instanceof User ) { this.name = first + " " + last; } else return new User(first, last); }
The two tricky lines are the first and the last. Let’s examine those in depth.
if ( this instanceof User ) {
This statement seems straightforward but takes a little bit of consideration. What we’re doing is checking to see if we’re currently inside of an instantiated version of the function, rather than just the function itself. Let’s look at a simpler example:
function test(){ alert( this instanceof test ); } test(); // alert( false ); new test(); // alert( true );
Using this bit of logic we can now construct our structure within the class constructor. This means that if the ‘this instanceof User’ expression is true then we need to behave like we normally would, within a constructor (initializing property values, etc.). But if the expression is false, we need to instantiate the function, like so:
return new User(first, last);
It’s important to return the result, since we’re just inside a normal function at the point. This way, no matter which way we now call the User() function, we’ll always get an instance back.
// New Style: new User(); // => [object User] User(); // => [object User] // Old Style: new User(); // => [object User] User(); // => undefined
makeClass()
Ok, so this is all well-and-good, but let’s now wrap this together into a generic class constructor that we can reuse to build these types of functions. Something like the following should be sufficient:
// makeClass - By John Resig (MIT Licensed) function makeClass(){ return function(args){ if ( this instanceof arguments.callee ) { if ( typeof this.init == "function" ) this.init.apply( this, args.callee ? args : arguments ); } else return new arguments.callee( arguments ); }; }
Let’s reexamine our previous example and see how this function could be used:
var User = makeClass(); User.prototype.init = function(first, last){ this.name = first + " " + last; }; var user = User("John", "Resig"); user.name // => "John Resig"
So there you have it: A simpler syntax for classical-style object instantiation that easy for end users and is more robust and resilient to misuse.
Bonus! Let’s dive into the makeClass() function and see what makes it tick. Again, it’s deceptively simple (due to it’s length) but is actually a complex bit of code to wrap your mind around. Let’s break it down bit-by-bit.
The first part:
function makeClass(){ return function(args){ /* ... */ }; }
When we “make a class” all we’re doing is producing a function. Since, in JavaScript, “classes” (or, our closest approximation of what we call classes) is actually a heavily-enhanced function. Thus, calling makeClass just gives us a function to play with. All class-creation libraries do this (Prototype, base, et. al.).
if ( this instanceof arguments.callee ) { // Stuff ... } else return new arguments.callee( arguments );
Now, this code looks very similar to the code that we used before to check the “class” type. The distinction here is that we don’t know what the name of the class actually is. Thus, we have to cheat and “become generic”. When you’re inside of a function (remember, we’re inside the anonymous function that is now the representation of the class – we are the class) arguments.callee
is a reference to the function itself (in JavaScript 2 this will become the expression “this function”).
Now, that explains the first if statement, but let’s look at that last return statement. Notice that we’re instantiating an anonymous function, from inside of itself. I suspect that this will blow the mind of someone – and it should; this is what makes JavaScript a great language to work with. But note that we pass in ‘arguments’ as the first parameter to the class constructor. We’re cheating here. The issue is that you can’t actually call .apply() (or .call()) on the instantiation of a function (you can in JavaScript 2 by applying against the intrinsic::construct method). Thus, we pass along the arguments, collectively, as the first argument and then watch for those arguments to come in, named ‘args’.
if ( typeof this.init == "function" ) this.init.apply( this, args.callee ? args : arguments );
Finally, the last bit. Since we need to make the construction phase of the class generic, we defer that to an ‘init’ function property instead (Prototype uses ‘initialize’ instead, I like short words). Then we simply .apply() the function passing in the arguments that were transferred to us and the ‘this’ instance (but only if this this.init method exists, don’t want to require that a constructor exists). Additionally, since the arguments may have been collected and put into the first argument (previously) we need to verify that and check that it is an arguments variable with args.callee
.
And there you have it: Some very-simple class construction code that’s actually quite powerful and useful. Comment and feedback are appreciated.
Tim Connor (December 7, 2007 at 1:05 am)
John, yeah when I was writing js UI library stuff I’d use the return-a-new-instance-on-calling-the-class-function idiom heavily, for the reasons you mention, it worked well with Crockford-esque private/protected stuff, and because it also allowed, simplified chaining of calls.
And since I was attaching behavior to DOM objects, I’d also just attach the js object to the DOM object , and then subsequent calls would be “cached” since I had checks in my init special cases either returned the existing object or made a call to new and attached it. (warning, make sure to do cleanup of said circular refs, if you don’t want IE memory leak nightmares)
Sebastian Werner (December 7, 2007 at 1:21 am)
Wow, impressive article. We came up with a idea which is built on top of these. So the qooxdoo class declaration (as of 0.7) is not that different. We also use the prototype chain (including inheritance support). We have an optional constructor (and destructor). There are also a lot of other great features like support for static members, properties, mixins (like in Ruby, Python), interfaces (like in Java) etc.
Maybe some readers are interested. Take a look here: Class Declaration in qooxdoo. Even if one do not use qooxdoo completely it should be possible to only use the OO stuff stand-alone.
Kris Kowal (December 7, 2007 at 2:42 am)
A clarifying detail…
/me pulls out The ES-262 Tome.
In current JavaScript, the context object (“this”) is always the global scope (“window”) for non-member functions (functions that aren’t called with .call, .apply, new, or as members of another object). I believe that this is changing in ES4. The new behavior is to pull “this” off of the scope chain, so if you declare an anonymous function inside of a member function, “this” would always be the object that contains the member function. That is to say, “this” only gets added to a closure if you call a function as a method. Checking whether
this instanceof arguments.callee
is still the most robust solution.Also, another added benefit of declaring your classes with
makeClass
is that your program becomes more elegant and reusable. Classes and factory methods become interchangeable. Suppose that you had a function that accepts the User type so that it can construct users from first and last names. Now, you can pass a memoized version of the User constructor instead.These are just the first steps in constructing a full-featured type system if you want super-classes, overloading, mixins, implicit binding, private and protected data, callable objects, or implicitly fully qualified type names (for introspection/reflection). You really can have all of these things in JavaScript with some padding.
Great article: all frontenders should see this technique.
Brendan Eich (December 7, 2007 at 2:58 am)
Kris: nothing in John’s examples changes in ES4. The |this propagation| bug fix has to do only with a named inner function called from an outer function by its name. That’s not what is happening here, with a lambda (anonymous function) returned and called via new, or not.
/be
Alex (December 7, 2007 at 3:16 am)
Is there any benchmarking tests proving that the module pattern is much slower than prototype approach?
Thank you!
Jacob Rus (December 7, 2007 at 3:21 am)
Very nice article. Thanks.
Dustin Diaz (December 7, 2007 at 4:15 am)
I don’t think this style is a replacement for the module pattern. Plus, I think you misinterpreted it anyhow. They both have their place. I used the
Class.prototype { /* properties */ };
style when I wrote DED chain. Still. The module pattern was more or less a technique to hide members. You can do the same for this case:function MyClass() { };
MyClass.prototype = function() {
var privMembers = { /* stuff */ };
return { /* members */ };
}();
Neil (December 7, 2007 at 5:13 am)
if (this instanceof Foo) {
this.bar = baz;
} else
return new Foo(baz);
looks (IMHO) very ugly. I’d suggest
if (!(this instanceof Foo)) // instanceof has low precedence, sigh...
return new Foo(baz);
this.bar = baz;
Tore Darell (December 7, 2007 at 5:39 am)
I might have misunderstood how
new
works, but can’t you fake it, callingapply
orcall
on a constructor like this?function makeClass(){
return function(){
if ( this instanceof arguments.callee ) {
if ( this.init )
this.init.apply( this, arguments );
} else {
//the "object" function:
var F = function(){};
F.prototype = arguments.callee.prototype;
var o = new F();
arguments.callee.apply(o, arguments);
return o;
}
};
}
It might be slower than just passing
args
though, and it won’t be semantically different in any way.John Resig (December 7, 2007 at 8:22 am)
@All: Don’t worry, I love the Module Pattern very much – I’m going to be doing another post on some new code that I’ve been working on. But yes, I’ll produce some speed tests as well, as the repeated object initialization has huge overhead, compared to normal prototype instantiation.
@Dustin: I’m confused by your example code – those “private variables” would, indeed, be private – but they’d also be static (the same value for all instances of that “class”) – which isn’t quite the same thing.
function MyClass() { };
MyClass.prototype = function() {
var num = 1;
return { add: function(){ return num++; } };
}();
var a = new MyClass();
var b = new MyClass();
a.add();
// => 1
b.add();
// => 2
@Neil: Good call, that’s probably better for writing the long blocks. The low precedence thing irks me too.
@Tore: Good call – that would probably work nicely! It’s unfortunate that there’s the extra overhead of creating an extra function, setting it’s prototype, etc. – but yeah, it’s good to have that alternative shown here.
Phil Palmieri (December 7, 2007 at 9:15 am)
Hi John, great article… one question, perhaps im just reading to much into the sample
if you are going to go through writing the code to return an object without the operand why not throw an error instead and not allow it?
function User(first, last){
if ( this instanceof User ) {
this.name = first + " " + last;
} else
return new User(first, last);
}
//Simple example throwing error
function User(first, last){
if ( this instanceof User ) {
this.name = first + " " + last;
} else
alert("Error: User must be instantiated");
}
John Resig (December 7, 2007 at 9:32 am)
@Phil: Sure – that’s an option too! Of course, rather than intentionally doing nothing when the user makes a mistake, why not provide them the added benefit of functionality, instead? It’s just a preference, but I tend to prefer ‘feature’ over ‘reprimand’.
Kevin Le (December 7, 2007 at 11:05 am)
John,
It’s very nice that you educate us on what a nasty bug could cause if one forgets using the new keyword when calling a function constructor. I find your explanation to be very useful. But is it really that practical to adopt your solution in all situations? I mean if you don’t have just one but many classes, then you would have to call makeClass() that many times. I just wonder if all of this is worth the effort to protect myself or some developer in the group who might accidentally forget the new keyword or so that I could conveniently enjoy dropping the new keyword when calling the function constructor?
On contrary, I think it would cause at least some confusions to someone who has not yet run across this technique but happens to read the code with the new style.
David Serduke (December 7, 2007 at 12:03 pm)
I may have missed something but won’t init() have problems with the following?
var user = new User("John", "Resig");
I may be missing something but it looks like args would only be an array if new hadn’t been used. Otherwise args would just be the first parameter “John” and not an array at all. I have an idea but it probably isn’t the best solution since someone could pass in arguments on purpose, but it was the first one that came to mind.
function makeClass() {
return function (args) {
if (this instanceof arguments.callee) {
if (typeof this.init == 'function')
this.init.apply(this, (args.callee) ? args : arguments);
} else
return new arguments.callee(arguments);
};
}
David
Matt (December 7, 2007 at 1:35 pm)
Great read thanks, has made me rethink a lot of code I have been writing. One day if you are bored and feel like giving even more to the world, it would be great to see you do a breakdown similar to this of the whole jquery library structure. Not each method in it, but just discuss each block of code. I have been creating a library for my team to use as an abstraction layer on top of jQuery and would love to understand some of the big picture decisions in the library better. I recognized what you were discussing here right away from
var jQuery = window.jQuery = function(selector, context) {
// If the context is a namespace object, return a new object
return this instanceof jQuery ?
this.init(selector, context) :
new jQuery(selector, context);
};
And would love to see you discuss the rest of the library. I guess kind of a blue print of how to build a library in a way. Asking too much? Maybe.
Brad harris (December 7, 2007 at 1:38 pm)
I see this as causing more problems than solving in the long run. Its always better to educate a user than fix their problems silently for them. Sure, they won’t ever have to worry about the problem as long as this method of instantiation is provided for them, but as soon as they work on something that isn’t providing this, they’ll more than likely continue a bad habit and break stuff along the way.
Kris Kowal (December 7, 2007 at 3:03 pm)
@Brendan: Yes,
makeClass
will always work. My point was that the second code example wouldn’t be illustrative in any scope other than global.makeClass
even works in some funky edge cases, like where a lambda becomes a named function when you place it in a module and invoke it as a member of that module.this.userModule = new function () {
this.User = makeClass();
};
userModule.User();
In that case, User would receive “userModule” as the context object. John’s code handles that case quite nicely. When I was hand coding this kind of invocation, it always took the pattern:
this.User = function () {
if (this == window)
return new arguments.callee();
}
But, John’s code is even more robust than this since it will handle the unlikely case that someone does something silly like:
User.call(new Monkey())
Clearly, our users aren’t Monkeys, and John’s code gracefully ignores the provided context object.
John Resig (December 7, 2007 at 5:24 pm)
@Kevin and Brad: I definitely don’t recommend using this everywhere or for every “class”. It’s much more practical to use it in the situation encountered with jQuery (It’s both a callable function: jQuery(“div”) and a constructable function: new jQuery(“div”)). Users will see an infinitesimal speed up by doing ‘new jQuery(“div”)’ but, for convenience, it’s just simpler to allow them to do jQuery(“div”) and have it “just work”. I assume that there must be similar situations where the ‘new’ keyword would just be clunky – and this is a way to work around that.
@David: Good catch! I like your fix so I worked it in to the above code and added an explanation.
@Matt: Well, we’ll see how much I can cover, but I hope to continue covering cool, not frequently discussed, concepts that I’ve encountered – many of which are in jQuery. Maybe they can all be pulled together into a book of some sort, we’ll see!
@Kris: Thanks for the good feedback, I appreciate it!
Dustin Diaz (December 7, 2007 at 6:00 pm)
Yeah, true enough John. For primitive types, these private variables might not be wanted, but it would, however, be more useful for static functions in the same fashion in how we would inherit from an Object’s prototype.
g. griffin (December 7, 2007 at 6:32 pm)
no, its a bad thing, SISO (Shit In, Shit Out). If people don’t read documentations, so be it. As to the so called “new” programmers, you take away their oportunity of deeper understanding how javascript works.
if you give them “oh well, no matter what I write, it works”, it’s bad for personal development and secondly more important, application performance.
It’s nice, for people, who know, what they are doing and they would not use it for the sake of speed.
Jake (December 7, 2007 at 8:29 pm)
God, JavaScript is a funky little language. Very nice demo.
Paul Irish (December 7, 2007 at 8:47 pm)
@John, thanks for your explanation to Kevin and Brad. I’d love to see a tutorial on how to write code that’s performance-optimized for jQuery.
Many of us are now writing much of our apps with a javascript library and we understand that the convenience of the library provides a performance overhead in some cases, but I’m sure there are tricks like this that can produce faster code. :)
Chris (December 9, 2007 at 11:12 pm)
Nice article, pushing the boundaries as always. With all you have going on, where do you find the time? =)
Douglas Crockford (December 10, 2007 at 11:55 am)
JSLint enforces a convention that functions beginning with an initial cap MUST be called with the new prefix. If you use JSLint, you don’t need to pay for the runtime testing of this.
Kris Kowal (December 11, 2007 at 3:33 am)
@Brendan once again, I stand corrected. I wrote up an experiment and John’s example is, in fact, illustrative in all scopes. My mistake.
Wade Harrell (December 12, 2007 at 1:56 pm)
Previously I would have written something like:
(function($){
if(typeof(window.com)==="undefined"){window.com={};}
if(typeof(com.myDomain)==="undefined"){com.myDomain={};}
if(typeof(com.myDomain.site)==="undefined"){
com.myDomain.site=function(){
return{
init:function(){
$("#printPage").click(com.myDomain.site.printPageEVT);
$("#undoPrint").click(com.myDomain.site.undoPrintEVT);
},
printPageEVT:function(e){
if(!e){var e=window.event;}
(typeof(e.preventDefault)!=="undefined")?e.preventDefault():e.returnValue=false;
var theEl=(e.target)?e.target:e.srcElement;
if(theEl){
if(e.type.toLowerCase()==="click"){
com.myDomain.site.setActiveStyleSheet("Print Preview");
window.print();
}
}
},
undoPrintEVT:function(e){
if(!e){var e=window.event;}
(typeof(e.preventDefault)!=="undefined")?e.preventDefault():e.returnValue=false;
var theEl=(e.target)?e.target:e.srcElement;
if(theEl){
if(e.type.toLowerCase()==="click"){
com.myDomain.site.setActiveStyleSheet("default");
}
}
},
setActiveStyleSheet:function(styleName){
$("link[rel*='style'][title]").each(function(i){
this.disabled=true;
if(this.getAttribute("title")===styleName){this.disabled=false;}
});
}
}
}();
}
$(document).ready(function(){
com.myDomain.site.init();
});
})(jQuery);
But in the context of this article the following would work as a replacement:
(function($){
var makeClass=function(){
return function(args){
if(this instanceof arguments.callee){
if(typeof(this.init)==="function")
this.init.apply(this,args.callee?args:arguments);
}else{
return new arguments.callee(arguments);
}
};
}
if(typeof(window.com)==="undefined"){window.com=makeClass();}
if(typeof(com.myDomain)==="undefined"){com.myDomain=makeClass();}
if(typeof(com.myDomain.site)==="undefined"){com.myDomain.site=makeClass();}
com.myDomain.site.prototype={
init:function(){
com.timeinc.direct2time.site=this;
$("#printPage").click(com.myDomain.site.printPageEVT);
$("#undoPrint").click(com.myDomain.site.undoPrintEVT);
},
printPageEVT:function(e){
/* same as above */
},
undoPrintEVT:function(e){
/* same as above */
},
setActiveStyleSheet:function(styleName){
/* same as above */
}
};
$(document).ready(function(){
com.myDomain.site();
});
})(jQuery);
Is there an advantage to one over the other?
Credits: The methods in the above example are based on http://www.alistapart.com/articles/printtopreview and http://kelvinluck.com/article/switch-stylesheets-with-jquery
John Resig (December 12, 2007 at 5:11 pm)
@Wade: The thing that irks me about the above code (and about most Class-like Object-Oriented JavaScript code) is that it doesn’t really seem to make use of the innate features of that particular style. For example, you’d be just as well off as doing:
(function($){
function printPageEVT(){
setActiveStyleSheet("Print Preview");
window.print();
}
function undoPrintEVT(){
setActiveStyleSheet("default");
}
function setActiveStyleSheet(styleName){
$("link[rel*='style'][title]").each(function(){
this.disabled = this.getAttribute("title") != styleName;
});
}
$(function(){
$("#printPage").click(printPageEVT);
$("#undoPrint").click(undoPrintEVT);
});
})(jQuery);
Three benefits: The syntax to call the functions is much simpler, you don’t have to define any namespacing, and there’s no namespace pollution at all (all your code is contained within the closure)!
On the other hand, if you picked that code just as a random structure example (sorry if that was the case) then I’d probably recommend something closer to the second style, but with only one makeClass() (as the first two should just be {} instead).
Wade Harrell (December 12, 2007 at 6:03 pm)
@John: Thanks, and I see your point. I had gotten in the habit of enforcing namespacing from working with JSAN and building ‘application’ single HTML sites that bootstrap themselves with js files for each view or functionality set (although not doing that too frequently these days). Definitely overkill on a site with less than 5 js files and multiple HTML.
I thought it interesting that using makeClass allows for com.myDomain(), com.myDomain.site(), and com.myDomain.site.undoPrintEVT() to coexist; but again it is overkill and I do not have a use case for it.
Philippe Rathe (December 15, 2007 at 3:10 pm)
Hi John I wonder how would you do inheritance with the User example on that page when using the makeClass() pattern?
Here is my expectation to create an OuterUser that inherits from User:
var User = makeClass();
User.prototype.init = function (first, last){
this.setName(first, last);
};
User.prototype.setName = function (first, last) {
this.name = first + " " + last;
};
var OuterUser = makeClass();
OuterUser.prototype = User.prototype;
OuterUser.prototype.init = function (planet, first, last){
this.planet = planet;
this.setName(first, last);
};
OuterUser.prototype.test = function (){
alert("test");
};
var outerUser = OuterUser("B612", "Petit", "Prince");
outerUser.name; // "Petit Prince"
What is a bit weird is that an “instance” of User can access the methods of OuterUser, but obviously not the “this”.
Don’t we have any other choice when working with prototypes augmentation?
Gracefully,
Philippe :O)
John Resig (December 16, 2007 at 11:42 am)
@Philippe: You would have to do it just like you would do normal inheritance, which would be the same as above only with this line:
OuterUser.prototype = new User();
Otherwise if you set the prototype’s equal to each other, you’re just overriding the original User init when you add the OuterUser init, which is a bad thing!
Bramus! (January 31, 2008 at 8:59 am)
Hi John,
just read Dan Webb’s post on Low Pro for jQuery and I – as an ex PrototypeJS user – must say that I like what he’s done on extending the $ Object with a class instantiator “klass” (see lines #51 up to #80).
Any chance this might see it into the main jQuery trunk? Reason I’m asking is that I find his approach more unbiased/less confusing than the ones you’ve mentioned here.
Regards,
Bram.
Bramus! (January 31, 2008 at 2:11 pm)
Looks like the link to the source file changed by now: It now is:
http://github.com/danwrong/low-pro-for-jquery/tree/master/src/lowpro.jquery.js?raw=true
Alex (February 26, 2008 at 10:00 am)
John, I’ve noticed that in jQuery-1.2.3 you do not use the makeClass approach. Is it for a reason? Or the jQuery approach is better? Which one would you recommend?
Also, I would like to have the makeClass function under the jQuery namespace for future jQuery versions:
jQuery.makeClass = function() {/*Your code*/}
Thank you!
Regards,
Alex Objelean.
Jed Schmidt (April 2, 2008 at 10:49 am)
Another neat tweak would be to pass the prototype methods to the function itself like this:
var makeClass = function(methods) {
var fn = function(args) {
if (!( this instanceof arguments.callee ))
return new arguments.callee( arguments );
if ( typeof this.init == "function" )
this.init.apply( this, args.callee ? args : arguments );
}; fn.prototype = methods; return fn;
}
so that you could create a class and its methods in one shot:
var User = makeClass({
init: function(first, last){
this.name = first + " " + last;
}
});