Some people seem to be a bit confused about the return value of a JavaScript function when used as a constructor. So what does «new Foo(some args)» evaluate to?
If the constructor returns an object then that is used; otherwise the return value is irrelevant and the newly constructed object is used.
The immediate and practical upshot this is that for a constructor it doesn’t matter whether you use «return» or «return this». I would say that a constructor’s normal job is to make objects so if you must return explicitly from a constructor then just use the simple «return» form.
The amusing and alarming upshot is that the behaviour of a function can be radically different according to whether it is invoked as a function or a constructor. Imagine you have a subtraction function:
function Sub(x, y) { return x-y }
Invoke it like «Sub(9,5)» and it returns 4. What does it do when used in «new Sub(9,5)»? Well, technically the function still returns 4, but because 4 is not an object that value is ignored and the newly constructed object is returned instead. A new
expression is guaranteed to evaluate to an object (or raise an exception).
Consider (at an interactive JavaScript prompt):
js> function Sub(x, y) { result = x-y return result } js> Sub.prototype.toString = function() { return 'I am a walrus' } js> Sub(9,5) 4 js> result 4 js> new Sub(100,1) I am a walrus js> result 99
When invoked as a constructor we get a new object that prints as «I am a walrus» because of the toString method in its prototype. I have deliberately changed Sub so that it writes to a global variable so that you can convince yourself that it is being called in the constructor case even though its return value is not being used.
Do not do this at home!
I am not recommending the above, I just find it an amusing way to illustrate an obscure feature of JavaScript.
I recommend that you make it crystal clear which of your functions are constructors and never encourage anyone to call them as a normal function. The fact that all functions can be used as constructors (and vice versa, any constructor can be invoked as a function) is an attractive nuisance. Sure, you can avoid it by being careful, but it’s too easy to do accidentally. Douglas Crockford has an article about the attractive nuisance of fallthrough in switch, which is where I got the term.
The usual convention, which seems very sensible to me, is to start constructors with a capital letter. That’s makes them look a bit more like classes too.
To some extent you can protect yourself against accidentially calling a constructor as a function. You can test whether this
is the global object (which it should be in a function) or not the global object (which it should be in a constructor). But only if you first remember what the global object is:
js> g=this [object global] js> function Foo() { if(this === g) { throw 'I am a constructor, not a function' } } js> new Foo() [object Object] js> Foo() uncaught exception: I am a constructor, not a function
Because the first OO language I learnt was Dylan I find that the artificial separation of constructors and ordinary functions (which may or may not return fresh objects) is also attractive nuisance. It’s an attractive nuisance that has worked its way (like fallthrough in switch) into many programming languages: C++, Java, JavaScript. Even Common Lisp has it, though I suspect that unwrapped uses of make-instance
are actively discouraged. In the other languages I mention, the idea that you might use something other than a constructor to get a fresh object is so unusual that there’s a name for this idea: factory method pattern!
Appendix: Standard References
All in ECMA 262 3rd edition.
Section 11.2.2 defines the semantics of the new operator. Which when used like this:
new Foo()
The semantics are to grab the Foo object and call its [[Construct]] method. When Foo is a constructor that you defined yourself, that is a JavaScript function, the [[Construct]] method is described in section 13.2.2 and has roughly the behavious I describe above: construct a «new Object()» (and let it be this
); call the function; if it yields an object then use it, otherwise use this
.