Fixing Object Instances in JavaScript

By | | JavaScript & CSS

JavaScript objectsDo you know your JavaScript? Take a look at the following code sample and work out what value is shown in the final alert statement …


// object constructor
function ObjectConstructor(a, b, c) {
	this.A = a;
	this.B = b;
	this.C = c;
	this.Total = a + b + c;
}
var obj = ObjectConstructor(1, 2, 3);
alert(obj.Total);

Hands up everyone who answered “6.”

Sorry, you’re wrong. The answer is … nothing — or an error stating that ‘obj’ is undefined. So what’s gone wrong?

The simple answer is that we’ve forgotten the ‘new’ operator so an object instance is never created. The statement should be:


var obj = new ObjectConstructor(1, 2, 3);

It’s an easy mistake to make. Novice developers are unlikely to spot the missing operator because the code looks almost identical. Even experienced coders could find it tough to debug (especially since many assume JavaScript is a procedural programming language … which it can be, if you choose to write it that way).

The main problem is that var obj = ObjectConstructor(1, 2, 3); is a perfectly valid JavaScript statement and the interpretor engine will not throw an error. In that context, the value of obj is set to the value returned from the function ObjectConstructor; since no value is returned, obj remains “undefined” (a top-level JavaScript property).

This is unlikely to become a major problem if you’re developing, testing and debugging your own code. However, it could be a different matter when you’re providing a library or API to thousands of third-party developers. At some point, someone, somewhere, will miss that ‘new’ operator and they will blame your code rather than theirs.

Fortunately, JavaScript is a flexible language. We can fix our constructor so an object is correctly created even when the ‘new’ operator is omitted:


// object constructor
function ObjectConstructor(a, b, c) {
	if (!(this instanceof arguments.callee)) {
		return new ObjectConstructor(a, b, c);
	}
	this.A = a;
	this.B = b;
	this.C = c;
	this.Total = a + b + c;
}

The additional ‘if’ statement at the top of the constructor checks whether ‘this’ is an instance of the object and returns one if necessary. The code var obj = ObjectConstructor(1, 2, 3); will now set obj to an object instance and “6″ will be output by the alert statement.

Have you ever encountered this problem in your code or when using another library?

Written By:

Craig Buckler

Craig is a Director of OptimalWorks, a UK consultancy dedicated to building award-winning websites implementing standards, accessibility, SEO, and best-practice techniques.

Website
>> More Posts By Craig Buckler

 

{ 23 comments }

Juan Mendes November 19, 2009 at 2:35 am

@DBJ.ORG Your solution returns an object of type object, not of type X, therefore hindering the use of instanceof and inheritance.

This is a valid solution, overkill for internal projects, but useful for libraries to keep your users from shooting themselves in the foot. You should do it anytime you don’t need major hacks. However, the solution as stated requires copy/pasting of code into every constructor, ouch!

DBJ.ORG November 12, 2009 at 10:51 pm

Since You/we/me are developers of a library, assumption is we should know better then library users ?

function X (a_,b_,c_) {

return {
a : a_, b :b_, c : c_, total : a_ + b_ + c_
}

} ;

(new X(1,2,3).total) === (X(1,2,3).total)

/*
true
*/

Therefore we will use correct development idioms, to shield ourselves in a most efficient way, from possible incorect use of our library.

Craig Buckler October 23, 2009 at 9:38 pm

As mentioned, this is not a solution for all projects. However, we all implement code that validates, fixes or sets defaults for parameters — is this solution much different? At the very minimum, it will reduce your support burden and that’s never a bad thing.

If I understand brothercake correctly, he’s saying that we’re making an assumption about what the developer’s doing. However, in this case, there’s little ambiguity. A reference can still be made, but any call using parenthesised arguments must always create a new object. We never want this constructor to run as a function and implementing the fix stops that happening.

Ryan Wray October 23, 2009 at 12:23 am

Damm new lines are gone (looked correct in preview):

// Make a reference to the constructor
var objRef = ObjectConstructor;
// Next 2 statements are equivalent
var obj1 = objRef(1, 2, 3);
var obj2 = new objRef(1, 2, 3);

Ryan Wray October 23, 2009 at 12:21 am

What if the user intended to refer to the object constructor and not to create an instance, for example, to create a reference to it.

I’m not disagreeing that you should be careful with changing the way the language behaves, but could you expand on this point?

Creating a reference still works:


// Make a reference to the constructor
var objRef = ObjectConstructor;
// Next 2 statements are equivalent
var obj1 = objRef(1, 2, 3);
var obj2 = new objRef(1, 2, 3);

Of course, if you actually expect the function to do something else, you wouldn’t get anything. But that’s the case, references or not. References are not broken under this scheme.

brothercake October 22, 2009 at 11:03 pm

Why not do so? Because the fix may be worse than the original problem. You’re still second-guessing what was intended.

What if the user intended to refer to the object constructor and not to create an instance, for example, to create a reference to it.

Craig Buckler October 22, 2009 at 9:33 pm

The demands of each application will be different. If it’s your own internal code, you don’t need this ‘fix’. If you’re providing a complex API to proficient developers, an error may be more appropriate.

However, a jQuery-like library open to anyone could benefit from the technique. If a problem’s fixable, why not do so? After all, we developers can not be held responsible for the bad coding practices of others … but we do have a duty to minimize the level of support required for our products.

AnilG October 22, 2009 at 7:52 am

What is the world coming to? Programmers are introducing additional code to help other programmers who can’t program? I’m going in the opposite direction: trying to remove bloat from my client-side coding and make it more efficient.

As others have said, at least just throw an error if you’re going to do this, but otherwise let them read a book on programming and test their code. If they can’t work out when to use the ‘new’ operator there’s probably worse things going wrong too.

rozner October 21, 2009 at 9:11 pm

I agree with tgoyer. The alert would be a much better option. While the code is an interesting fix, it promotes bad coding. I’d rather people who think they can create an object with “new” would just learn that they need to use “new”. I spotted the error right away. For a second I thought, hmm, maybe Javascript allows object creation with the “new” keyword, then I read on.

ray October 21, 2009 at 8:45 pm

I like the idea of throwing an error, but would avoid alerts incase you’re creating many instances quickly – next stop alert-hell!

brothercake October 21, 2009 at 6:54 pm

Throwing or alerting an error is a vastly better idea than trying to second-guess the intended syntax. I appreciate what you’re trying to do Craig, but you could create as many problems as you solve with an approach like that.

danNL October 21, 2009 at 1:03 pm

Nice tgoyer :)

.. and might as well throw it as an error then


if ( !( this instanceof arguments.callee ) ) {
throw( "You must instantiate this object with the 'new' keyword." );
}

which would be caught with something along the lines of:


try {
var obj = ObjectConstructor(x, y, z);
} catch(e) {
// use pre-defined error handler method or ...
alert(e);
}

Chris October 21, 2009 at 9:42 am

I think only half the problem is explained, it’s not just that you don’t have an object ref , it’s also now that you have added all the properties in the object to the global window object. Run the script and call alert with window.Total as the argument. Ta Da..

tgoyer October 21, 2009 at 7:36 am

For those that are concerned about furthering poor coding practices, you could always change the constructor challenge to something like:


if ( !( this instanceof arguments.callee ) ) {
alert("You must instantiate this object with the 'new' keyword.");
}

tgoyer October 21, 2009 at 7:35 am

For those that are concerned about furthering poor coding practices, you could always change the constructor challenge to something like:


if ( !( this instanceof arguments.callee ) ) {
return new ObjectConstructor( a, b, c );
}

danNL October 21, 2009 at 6:54 am

Nice :)

… might you also want to cover then the typing of the variables and use the assigned variables in the this.Total attribute?

e.g, what if somewhere in the app the following variables are created at various points:
var x = 1;
var y = ’2′;
var z = 3;

Then, a bit later you use the constructor:
var obj = ObjectConstructor(x, y, z);

Above you’d get an alert of “123″, like this, you might avoid these potential problems as well:
function ObjectConstructor(a, b, c) {

if ( !( this instanceof arguments.callee ) ) {
return new ObjectConstructor( a, b, c );
}

this.A = parseFloat(a);
this.B = parseFloat(b);
this.C = parseFloat(c);
this.Total = this.A + this.B + this.C;

}

Craig Buckler October 21, 2009 at 6:44 am

@Jasconius
This could be useful in code that will be shared. Anyone using jQuery (and probably some other libraries) are already using techniques of this type. jQuery’s is based around the $ object, but you don’t need to add ‘new’ to use it.

Dave Transom October 21, 2009 at 6:31 am

It’s a nice tip, but I agree with Jasconius.

A benefit of having the “new” keyword is that it shows you a new instance is being created and can help with readability (and understanding by others).

Jasconius October 21, 2009 at 5:54 am

I have to be honest I’ve never run into this problem. “new” is basic OOP. You’re simultaneously providing a failsafe against an object quirk in JS using an advanced technique that only people who wouldn’t have this problem to begin with could understand.

Odds are if you are using faux objects in JS, you already understand “new”, and that’s a generous assessment.

They aren’t broken, either. The reason that original code works is to support JS’s event syntax which allows such things as

object.onEvent = function(){...}

itpastorn October 21, 2009 at 4:05 am

It might be worth mentioning that arguments.callee is not allowed in ECMAScript 5th edition strict mode.

Craig Buckler October 21, 2009 at 3:20 am

Hi madr,

The module pattern has a slightly different use — it’s a singleton encapsulated in a module that cannot conflict with any other modules. You could still create objects within those modules or expose them so they can be used by other code/modules.

madr October 21, 2009 at 2:25 am

I personally prefer to solve problems like this with the Yahoo Module pattern, but anyway – thanks for sharing! Great tip.

RussellUresti October 21, 2009 at 2:15 am

That is an interesting solution. Personally, I think I’m more for alerting a debug error when bad code is written, rather than writing a solution that makes bad code work, but this is pretty darn clever.

Comments on this entry are closed.