Home > Uncategorized > Exploring constructors in Javascript

Exploring constructors in Javascript

I came upon this Javascript tutorial by jQuery inventor John Resig (nod to John Gruber). It covered several advanced Javascript topics with the goal of explaining to the reader the meaning of a particularly esoteric bind method from prototype.js. It was fun, and I followed most of it pretty well, although I got a little lost by the end in the section about Enforcing Function Context.

The tutorial included a lesson on pages 3538 about Javascript constructors which interested me.

First a recap of basic Javascript OOP. In Javascript, objects are defined via functions. To construct an instance of this function, you use the new operator, like so:
function foo(x,y) {this.x = x; this.y = y; }
var instanceOfFoo = new foo(1,2);

Whereas if you want to use a function as a function, you simply call it, like so:

function bar(x,y) {return x + y; }
var valueOfBar = bar(1,2); //this is the standard function call
valueOfBar = bar.call(this,1,2); //this is a more reflective call
valueOfBar = bar.apply(this,[1,2]); //this is even more reflective

So new functionName() to construct an instance of functionName, the OOP object constructor usage of a Javascript function. And just functionName() to to call the function and gets its return value, the functional usage of a Javascript function. Note that if a function is to be used in the functional manner, it must include a return statement; otherwise the value of the function all will be undefined.

The two uses of a Javascript function have different syntax and different semantics. And if you use one where you’re supposed to use the other, your code can fail in unexpected ways. In particular, if you accidentally leave of the keyword new, you won’t get an object, instead you’ll get the return value of the function, or else undefined if there is no return statement.

In addition to the standard Javascript function call, I listed two additional ways to execute a function call which could be said to be more reflective, ie instead of requiring you to write the identifier for the function statically in the source code, it may be determined dynamically at runtime. The first of the two reflective function calls is the Function.call method, which requires you to list the parameters explicitly. The second is the Function.apply method. The second is the more reflective of the two, because it takes an array containing all the arguments, which can be constructed at runtime, rather than requiring they be listed explicitly in the time.

Of note for this blog post is that there isn’t a builtin method like Function.apply which works when using the function as an object constructor. There’s some syntactical incompatibility between the function call and the object constructor which precludes from saying something like new foo.apply(array).

End elementary Javascript recap.

So in the Javascript tutorial, Resig demonstrates some code which ensures that even if you accidentally leave off the new keyword when constructing an object:

function User(first, last){

if ( !(this instanceof User) )
return new User(first, last);

this.name = first + " " + last;
}

The addition of the two lines of code in red give us the functionality to preclude calling this function as a function instead of an object constructor. So if the context in the function is an instance of the calling function itself, then the function was called correctly as constructing an instance of the object. Otherwise, it re-calls the function correctly with the new keyword. Pretty straightforward.

But this code is too static. First we have to reference the particular function we’re in. Then we reference it again, as well as the argument list. If you wanted to rely on this functionality, you’d have to include these two lines in every object constructor in your code, and you’d have to modify the lines for the particular function name and argument list. A massive violation of the principle of DRY.

So then Resig changes the first of the two lines to remove the reference to the function:

if ( !(this instanceof arguments.callee) )
return new User(first, last);

arguments is an array-like object available within the scope of every function. (To make an honest array out of arguments, use Array.prototype.slice.call(arguments)). It contains a list of all the arguments passed to the function (arguments[i] is the ith argument passed to the function), as well as a reference to the function that was called (arguments.callee). We compare the function to the context of the function. The context is available in the function via the this object. If you call the function as an object constructor, then this stores the object instance being constructed, and that object is an instance of function which was called. This this instanceof arguments.callee is true. On the other hand, if you call the function as a standard function call, then this is the global context, usually the browser window where the code is executing, which is, needless to say, not an instance of the function called. So this instanceof arguments.callee is false.

So in the first of the two lines of code, we've successfully removed the reference to the particular function by replacing it with arguments.callee. But it's still present in the second line. We haven't really solved the problem at all. This is where Resig leaves the issue, but I wondered if we could do better. It turns out that we can also replace the reference to the function in the second line:

if ( !(this instanceof arguments.callee) )
return new arguments.callee(first, last);

This is a little better, we've removed all explicit references to the function. However, we still don't have complete generality, because in line two, we still have to list explicitly the arguments to the function, so the code can't be reused without modification. But since we have at our disposal an array containing all the arguments, I thought we should be able to accomplish it. Just like Function.apply allows you to make a function call with arguments taken from the contents of an array, we need something analogous for object instantiation. Something like new foo.apply(this,arguments).

Unfortunately, the obvious guess doesn't work, and after fucking around with different options for a while, I couldn't come up with anything that did. So I turned to The Google, which as always, had the answer. Someone poses the exact problem I'm investigating on stackoverflow, and Matthew Crumley posts a solution:

function construct(constructor, args) {
function F() {
return constructor.apply(this, args);
}
F.prototype = constructor.prototype;
return new F();
}

function User(first, last)
{
if ( !(this instanceof arguments.callee) )
return new construct(arguments.callee,Array.prototype.slice.call(arguments));
this.name = first + " " + last;
}

I've tested this out and it does the job. I imagine perhaps adding it to the Function prototype, so that every function can optionally call it to ensure that it's only used as an object constructor. I do have to say though, I haven't been able to completely understand how the construct function works. It seems to rely on knowing how Javascript internally turns a function into an object, or some other magic that is eluding me.

It's mostly a moot discussion for me. I don't think I would make use of this function. Although I feel like Javascript ought to have an apply method for constructors, or better yet the constructor syntax should be more natural, somehow so that the two uses of the Javascript function are not distinct. But whatever, this is the way Javascript works, and I feel like we should learn it, rather than forcing it to change, rather than correcting it. So I don't see using this method. But it is instructive as a purely academic exercise to learn how it works and think about how it can be extended.