Is JavaScript’s “new” keyword considered harmful?

Crockford has done a lot to popularize good JavaScript techniques. His opinionated stance on key elements of the language have sparked many useful discussions. That said, there are far too many people that take each proclamation of “bad” or “harmful” as gospel, refusing to look beyond one man’s opinion. It can be a bit frustrating at times.

Use of the functionality provided by the new keyword has several advantages over building each object from scratch:

  1. Prototype inheritance. While often looked at with a mix of suspicion and derision by those accustomed to class-based OO languages, JavaScript’s native inheritance technique is a simple and surprisingly effective means of code re-use. And the new keyword is the canonical (and only available cross-platform) means of using it.
  2. Performance. This is a side-effect of #1: if I want to add 10 methods to every object I create, I could just write a creation function that manually assigns each method to each new object… Or, I could assign them to the creation function’s prototype and use new to stamp out new objects. Not only is this faster (no code needed for each and every method on the prototype), it avoids ballooning each object with separate properties for each method. On slower machines (or especially, slower JS interpreters) when many objects are being created this can mean a significant savings in time and memory.

And yes, new has one crucial disadvantage, ably described by other answers: if you forget to use it, your code will break without warning. Fortunately, that disadvantage is easily mitigated – simply add a bit of code to the function itself:

function foo()
{
   // if user accidentally omits the new keyword, this will 
   // silently correct the problem...
   if ( !(this instanceof foo) )
      return new foo();
   
   // constructor logic follows...
}

Now you can have the advantages of new without having to worry about problems caused by accidentally misuse.

John Resig goes into detail on this technique in his Simple “Class” Instantiation post, as well as including a means of building this behavior into your “classes” by default. Definitely worth a read… as is his upcoming book, Secrets of the JavaScript Ninja, which finds hidden gold in this and many other “harmful” features of the JavaScript language (the chapter on with is especially enlightening for those of us who initially dismissed this much-maligned feature as a gimmick).

A general-purpose sanity check

You could even add an assertion to the check if the thought of broken code silently working bothers you. Or, as some commented, use the check to introduce a runtime exception:

if ( !(this instanceof arguments.callee) ) 
   throw new Error("Constructor called as a function");

Note that this snippet is able to avoid hard-coding the constructor function name, as unlike the previous example it has no need to actually instantiate the object – therefore, it can be copied into each target function without modification.

ES5 taketh away

As Sean McMillan, stephenbez and jrh noted, the use of arguments.callee is invalid in ES5’s strict mode. So the above pattern will throw an error if you use it in that context.

ES6 and an entirely harmless new

ES6 introduces Classes to JavaScript – no, not in the weird Java-aping way that old-school Crockford did, but in spirit much more like the light-weight way he (and others) later adopted, taking the best parts of prototypal inheritance and baking common patterns into the language itself.

…and part of that includes a safe new:

class foo
{
  constructor()
  {
    // constructor logic that will ONLY be hit 
    // if properly constructed via new
  } 
}

// bad invocation
foo(); // throws, 
// Uncaught TypeError: class constructors must be invoked with 'new'

But what if you don’t want to use the new sugar? What if you just want to update your perfectly fine old-style prototypal code with the sort of safety checks shown above such that they keep working in strict mode?

Well, as Nick Parsons notes, ES6 provides a handy check for that as well, in the form of new.target:

function foo()
{
  if ( !(new.target) ) 
     throw new Error("Constructor called as a function");
   
  // constructor logic follows...
}

So whichever approach you choose, you can – with a bit of thought and good hygiene – use new without harm.

Leave a Comment