Working around the keyword new in JavaScript

Make constructor functions work without "new" keyword in callbacks.

JavaScript EcmaScript5 provides useful array iterators that express the desired logic in concise and natural way. We call a function on each argument:

1
2
3
4
function positive(x) {
return x > 0;
}
console.log([2, -10].map(positive)); // [true, false]

Constructor functions on the other hand require new keyword, which makes their use as callbacks impossible

1
2
3
4
function Tree(species) {
this.species = species;
}
['Maple', 'Oak'].map(new Tree); // TypeError

Usually we work around this problem by creating an explicit callback function

1
2
3
['Maple', 'Oak'].map(function (kind) {
return new Tree(kind);
});

I do not like this approach:

  • we create a tiny untested function just to get around the language's syntax.
  • The meaning gets lost in all the boilerplate code.

There are two approaches we can take

Utility function

We can create an utility function to construct objects and put the new keyword there. I omit details about passing the right number of arguments to the constructor.

1
2
3
4
5
function construct(Constructor, arg) {
return new Constructor(arg);
}
var makeTree = construct.bind(null, Tree);
['Maple', 'Oak'].map(makeTree);

Function makeTree does not really have to be tested if the function construct has been well tested.

I placed function newDate into d3-helpers that helps avoid using new when constructing Date objects.

Smart(er) constructor

One can write logic into the JavaScript constructor to detect if the function has been called with new keyword or not. John Resig describes it as Simple Class Instantiation. In our case, we should modify Tree function to this

1
2
3
4
5
6
7
8
9
function Tree(species) {
if (this instanceof Tree) {
this.species = species; // new Tree('oak');
} else {
return new Tree(species); // Tree('oak');
}
}
// Tree used as callback to construct new instances
['Maple', 'Oak'].map(Tree);

Which method you prefer is up to you. I like the smarter constructor, because it helps avoid leaving new by accident.

1
2
var oak1 = new Tree('oak');
var oak2 = Tree('oak');