function Point(x,y) {
this.x = x;
this.y = y
}
// This is good
var pt = new Point(20,30);
// This is not
var pt2 = Point(20,30);
You'll notice when you don't use new, pt2 is undefined after the call. Even worse, we've added two variables to the global scope, x, y. That is because if you call a function without specifying the context, the browser passes in the global object, which is "window";
The first attempt to fix this is the following
function Point(x,y) {
if (this instanceof Point) {
this.x = x;
this.y = y
} else {
return new Point(x,y);
}
}
// This is good
var pt = new Point(20,30);
// This is OK also
var pt2 = Point(20,30);
However, that seems like a lot of boiler plate code to add to every constructor. How can we abstract that? Here's what I came up with.
/**
* Wraps the passed in constructor so it works with
* or without the new keyword
* @param {Function} realCtor The constructor function.
* Note that this is going to be wrapped
* and should not be used directly
*/
function ctor(realCtor){
// This is going to be the actual constructor
return function wrapperCtor(){
var obj; // object that will be created
if (this instanceof wrapperCtor) {
// Called with new
obj = this;
} else {
// Called without new. Create an empty object of the
// correct type without running that constructor
surrogateCtor.prototype = wrapperCtor.prototype;
obj = new surrogateCtor();
}
// Call the real constructor function
realCtor.apply(obj, arguments);
return obj;
}
}
/**
* A function that does nothing, so we can create objects
* of a prototype without running unnecessary code.
* See its usage above
*/
function surrogateCtor() {}
// Create our point contructor
Point = ctor(function(x,y){
this.x = x;
this.y = y;
});
// This is good
var pt = new Point(20,30);
// This is OK also
var pt2 = Point(20,30);
// It's even ok with inheritance, though a 3D point shouldn't
// really inherit from a 2D point, it's just to make a point...
Point3D = ctor(function(x,y,z){
Point.call(this, x, y);
this.z = z;
});
// Note that this is not the best way to setup inheritance.
// My next post will explain a better way
Point3D.prototype = new Point();
var pt3D = new Point3D(5,3,8);
// Outputs true
console.log(pt3D instanceof Point3D && pt3D instanceof Point);