Posts tagged with javascript

New/Scope Safe Constructors in OO JavaScript

One frequent problem when developing in Object Oriented JavaScript is how to handle the removal of the "new" keyword when creating an instance of a class. It's often expected that an object should be viably instantiated two ways, with the "new" keyword and without.

var part1 = new CarPart('Wheel');
var part2 = CarPart('Axel');

With a normal constructor, the above code won't act as expected. Assume our CarPart constructor looks as follows:

function CarPart(name) {
  this.name = name;
}

Since this is bound at runtime, omitting the new operator results in a late binding to the window object. Late binding in JavaScript is often resolved with the use of closures but can't be in this case. In the case where the new operator is omitted, this.name is effectively the same as window.name.

Creating new/scope safe constructors is very important. If you write a library, for instance, it can't be expected that all users will be familiar enough with OO JavaScript to include the new operator. As the creator of a library, a good API results in a certain degree of permission for users to do what they want. Constructors are a good example of such a permission.

How to fix that poor constructor

Our goal is to identify when the constructor was called without the new operator and then invoke the constructor properly. Observe the following code.

function CarPart(name) {
if(this instanceof CarPart) {
  this.name = name;
} else {
return new CarPart(name);
}
}

In this code, we evaluate if the this object is a CarPart and proceed as normal. If the this object is not a CarPart (ie, it's a window object), call the constructor again, this time with the new operator.

Now our constructor works properly with or without the new operator.

But what about inheritance?

Close observers would realize that a child of the CarPart class would not be able to successfully invoke the parent constructor because it assumes the caller is a CarPart. In the following example, our safe constructor (CarPart) will not set the name attribute on the Wheel object. It would instantiate a new CarPart.

function CarPart(name) {
if(this instanceof CarPart) {
  this.name = name;
} else {
return new CarPart(name);
}
}

function Wheel(radius) {
CarPart.call(this, 'Wheel');
this.radius = radius;
}

Note: Wheel should be made new/scope safe as well.

In the above example, within the Wheel constructor, CarPart.call is what invokes the parent constructor, known as constructor stealing. When CarPart's constructor is called, this instanceof CarPart will fail because this is an instance of Wheel.

The solution to this problem lies within the Wheel's prototype. If we set the following after the declaration of Wheel, the inheritance chain is correctly established and this instanceof CarPart will return true if this is a Wheel.

Wheel.prototype = new CarPart();

Happy constructing!

Posted by Mike Pack on 05/23/2011 at 10:29AM

Tags: javascript, oo, inheritance


Tuesday Tricks - JavaScript Timer Differentials

Timers in JavaScript can be either very simple or become very difficult. In most cases, timers are very simple, such as polling a resource to update content on a SPA (Single-Page Application). In this case, you don't really care when you get your updates from the server, as long as you get them within, say, 1 second of when you expect to receive a response.

But how about when you need a timer to execute at an exact moment in time (ie: a game)? Well, this isn't possible. The best JavaScript can do is guarantee that your function will be run. It doesn't guarantee that it will be run at a specific time. This is because there could be other processes running when you request your timeout, using setTimeout.

When you call setTimeout with a 5000 millisecond delay, it may run in exactly 5000 milliseconds and it may not, depending on what is being processed and what is in the queue. If your setTimeout call gets placed in the queue for 50 milliseconds, your function will be run at 5050 milliseconds. The delay is unpredictable. It's a fact of cyber-life.

Timer Differentials

You can't tell how long your setTimeout call will remain in the queue (and offset your timing), but you can tell how long it remained in the queue after it has been executed. Be careful, it's only supported by Firefox.

setTimeout(function(diff) {
  if( diff > 0 ) {
// function was in the queue
} else if( diff < 0 ) {
// function was called early
} else {
//function was called on time
}
}, 5000);

This Tuesday's Trick

You probably never develop applications that only run in Firefox, but it's helpful to know that you can make your timing more accurate in the instance the user's browser is Firefox. setTimeout differential will help you do just that.

Posted by Mike Pack on 05/17/2011 at 12:23PM

Tags: tuesday tricks, javascript, timers