In my previous blog post on the subject of constructor functions, I mentioned that there is a convention in JavaScript to name constructor functions with an initial capital letter and ordinary functions with an initial lowercase letter. This way, since the language doesn’t stop you from using a constructor as an ordinary function, there is at least some kind of “warning” that you may be using a constructor and therefore need to use new.

The biggest problem is, if you do miss the new keyword, you are going to be corrupting the global object. Is there a way to write the constructor in order to avoid this? It turns out that there is.

Normally, when you write a constructor function you don’t return the object being constructed. The JavaScript language assumes that the object created by the use of the new keyword (called this within the function) is the object that’s going to be returned. It turns out that you can circumvent this feature by explicitly returning the object. In other words:

var Person = function (lastName, firstName) {
  this.lastName = lastName;
  this.firstName = firstName;
};

is interpreted in exactly the same manner as:

var Person = function (lastName, firstName) {
  this.lastName = lastName;
  this.firstName = firstName;
  return this;
};

In fact, it turns out that, providing that it is an object, you can return anything from a constructor. So, as a completely gratuitous duck-typed example:

var Person = function (lastName, firstName) {
  return {
    lastName : lastName,
    firstName : firstName
  };
};

var p = new Person("Bucknall", "Julian");
console.log(p instanceof Person); // prints false

In other words, the object being returned from the Person constructor is not an instance of Person at all (it just looks like one). The object created by the new keyword has been completely ignored (and it will be automatically garbage-collected later).

So what, you may be asking. What loony would return a completely different object than the constructor purports to create? Exactly, unless you know that the this object in the constructor is already an instance of the constructor. Take a look at this (pun intended):

var Person = function (lastName, firstName) {
  console.log(this instanceof Person); // prints true
  // other code
};

var p = new Person("Bucknall", "Julian");

In other words, by the time the constructor function has started to execute when called by new, JavaScript has already patched up the this object’s constructor and internal prototype properties. Although the object has not been completely constructed at the start of the function, it is of the correct type.

From that knowledge and the fact we can return any old object from a constructor, we get this:

var Person = function (lastName, firstName) {
  if (!(this instanceof Person)) {
    return new Person(lastName, firstName);
  }

  this.lastName = lastName;
  this.firstName = firstName;
};

var p = new Person("Bucknall", "Julian");
console.log(p instanceof Person); // prints true
var p = Person("Bucknall", "Julian");
console.log(p instanceof Person); // prints true

In other words, we can easily write a constructor that returns a properly constructed object, whether we use the new keyword or not.

Album cover for Whatever You Say, Say NothingNow playing:
Deacon Blue - Last Night I Dreamed of Henry Thomas
(from Whatever You Say, Say Nothing)



Posts on similar topics...

4 Responses

  • Thu 17 Feb 2011
  • 11:10 AM
  •  avatar #1

Will Alexander said...

Great tip! It is too easy to leave the new out and when you do, it may generate errors far removed from the constructor.

Is there a way you can use apply() so it will pass in arguments of arbitrary length? Would seem to apply (punny!) when chaining constructor calls and the parent expects more params than your subclass needs.

This would fail the instanceof check, but seem to work otherwise

return Person.apply({},arguments)

  • Sun 01 May 2011
  • 7:44 PM
  •  avatar #2

jmweb said...

Instead of using instanceOf can't we just make sure that this does not refer to the global object?

Something like if(this === window){return new Person();}

  • Mon 02 May 2011
  • 6:46 AM
  • julian m bucknall avatar #3

julian m bucknall said...

jmweb: Indeed, if you are writing JavaScript just for the client, you could test "this" against "window". (On the server, there is no global object property called "window", so you'd have to do something else.)

Cheers, Julian

  • Mon 02 May 2011
  • 7:17 AM
  • julian m bucknall avatar #4

julian m bucknall said...

Will: (Sorry, somehow I missed your comment before.) An interesting question and something I didn't consider. Let's suppose that the constructor function is defined in such a way that there are optional parameters. How would you write the function such that if it's not invoked via "new" you could invoke it internally so that all the parameters are passed in. At first blush you can't use apply/call since they don't set up the new object correctly. Your idea of passing in an empty object (and presumably altering the code to check for an empty object) might work. I'll have to think on it.

Cheers, Julian

Leave a Response

Some MarkDown is allowed, but HTML is not. (Click to learn more.)

  • Emphasize with italics: surround word with underscores _emphasis_
  • Emphasize strongly: surround word with double-asterisks **strong**
  • Link: surround text with square brackets, url with parentheses [text](url)
  • Inline code: surround text with backticks `IEnumerable`
  • Unordered list: start each line with an asterisk, space * an item
  • Ordered list: start each line with a digit, period, space 1. an item
  • Insert code block: start each line with four spaces
  • Insert blockquote: start each line with right-angle-bracket, space > Now is the time...

Extras

Search

About Me

I'm Julian M Bucknall, an ex-pat Brit living in Colorado, an atheist, a microbrew enthusiast, a Volvo 1800S owner, a Pet Shop Boys fanboy, a slide rule and HP calculator collector, an amateur photographer, a Altoids muncher.

DevExpress

I'm Chief Technology Officer at Developer Express, a software company that writes some great controls and tools for .NET and Delphi. I'm responsible for the technology oversight and vision of the company.

Validation

Validate markup as HTML5 (beta)     Validate CSS

Bottom swirl