Sunday, April 15, 2012

Singleton in JavaScript with Google Closure library and compiler

I'm writing a small WebApp. I want to a create singleton. Probably there is a dozen different way to implement it in JavaScript. As I became a fan of Google technologies I will show two solutions how it can be easily implemented using Google Closure.


I see two possible ways:

  • As a namespace, e.g. wookie.sun;
  • As a real singleton class.

Let's consider the first solution:

goog.provide('wookie.sun');

/**
 * Initialie the Sun. If the sun was already initialized it 
 *   will throw an exception.
 * @param {number} a first number.
 * @param {number} b second param.
 */
wookie.sun.init = function(a, b) {
  if(!wookie.sun.initialized_) {
    wookie.sun.a = a;
    wookie.sun.b = b;
    wookie.sun.initialized_ = true;
  } else {
    throw "wookie.sun already initialized!";
  }
};

/**
 * @private
 */
wookie.sun.initialized_ = false;

/**
 * @private
 */
wookie.sun.a_ = 0;

/**
 * @private
 */
wookie.sun.b_ = 0;

/**
 * Returns magic number. If the module wasn't initialized will return 0.
 * @returns {number} magic number computed for the a and b.
 */
wookie.sun.getMagicNumber = function() {
  return wookie.sun.a_ * wookie.sun.b_;
};

And it won't be possible to call:
var x = new wookie.sun();

And the second solution (C++ style).

goog.provide('wookie.sun');

/**
 * @private
 */
wookie.sun = function() {
};

/**
 * @private
 */
wookie.sun.instance_ = null;

/**
 * @private
 */
wookie.sun.initialized_ = false;

/**
 * @private
 */
wookie.sun.prototype.a_ = 0;

/**
 * @private
 */
wookie.sun.prototype.b_ = 0;

/**
 * Returns magic number. If the module wasn't initialized will return 0.
 * @returns {number} magic number computed for the a and b.
 */
wookie.sun.prototype.getMagicNumber = function() {
  return wookie.sun.a_ * wookie.sun.b_;
};

wookie.sun.prototype.init = function(a, b) {
  if(!this.initialized_) {
    this.a = a;
    this.b = b;
    this.initialized_ = true;
  } else {
    throw "instance of wookie.sun has been already initialized!";
  }
};

/**
 * @return {wookie.sun} only instance of wookie sun class.
 */
wookie.sun.getInstance = function() {
  if (!wookie.sun.instance_) {
    wookie.sun.instance_ = new wookie.sun();
  }
  return wookie.sun.instance_;
};
In that case instead of:
var x = new wookie.sun();
One needs:
var x = wookie.getInstance();
x.init(1,2);
x.getMagicNumber();

Of course, the constructor won't be available only when we compile code, in uncompiled JavaScript everything will work as unintended.

Notification: idea with init and throwing exceptions isn't the smartest. Would be probably better to return (instance, created), but in JS it's not so easy to return tuple as in Python or Dart.
Notice: it's impossible to implement Singleton and then only extend Singleton class. This is because static methods are treated by Closure Compiler as a functions and their names are shortend. Additionally goog.inherit doesn't inherit static methods and fields.
Some links for further reading:

No comments:

Post a Comment