Fixing Object Instances in JavaScript

Contributing Editor

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?

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • RussellUresti

    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.

  • madr

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

  • http://www.optimalworks.net/ Craig Buckler

    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.

  • http://keryx.se itpastorn

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

  • Jasconius

    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(){...}

  • Dave Transom

    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).

  • http://www.optimalworks.net/ Craig Buckler

    @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.

  • http://www.pennlinepublishing.com danNL

    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;

    }

  • tgoyer

    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 );
    }

  • tgoyer

    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.");
    }

  • Chris

    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..

  • http://www.pennlinepublishing.com danNL

    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);
    }

  • http://www.brothercake.com/ brothercake

    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.

  • ray

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

  • rozner

    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.

  • AnilG

    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.

  • http://www.optimalworks.net/ Craig Buckler

    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.

  • http://www.brothercake.com/ brothercake

    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.

  • Ryan Wray

    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.

  • Ryan Wray

    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);

  • http://www.optimalworks.net/ Craig Buckler

    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.

  • DBJ.ORG

    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.

  • Juan Mendes

    @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!