JavaScript Scope Blues

Posted in Development by MB on 07/18/08

I’ve been working out some methods of making uniquely namespaced JavaScript objects.

I’m trying to handle a situation where there could be multiple instances of the same object or functions on the same page with the same name. The idea is to prevent one function, variable, or object accidentally writing over another because they have the same name.

I settled on a factory pattern which accepts a namespace as a parameter, followed by an array or object of parameters.

I start with a factory function which does some simple checking for a namespace, and then calls a constructor function, assigning the resulting object to a global property of the window object.

This code assumes that “namespace” is a unique value.

function myObjFactory(namespace,objParameters)
{
	if (!namespace)
	{
		throw new Error("Null Parameter 'namespace'");
	}
	window[namespace] = new MyObj(objParameters);
}

So in JavaScript the way to create a custom object is to define a function, and call it with the “new” keyword, as above. Your function can accept any parameters you wish. These parameters can be used to set default values in your object, or operated on by internal functions.

In JavaScript everything is an object, including functions. Functions have a public property “caller” which holds a reference to the function or object which called the function. So in my example below, if I try to call my constructor function from anywhere but the function “myObjFactory” it will throw an error. This little trick makes it impossible to instantiate MyObj without myObjFactory, ensuring my design pattern doesn’t unravel.

function MyObj(objParameters)
{
	if (MyObj.caller != myObjFactory)
	{
		throw new Error("No public constructor for MyObj.
			 Use myObjFactory");
	}
	var shy = "private value";
	this.params = objParameters;
	this.arbitraryHandler = function(e) { return e;}
	this.publicFunction = function() { alert(shy);}
}

Notice that some members of this function are prefixed by “this”. Those values become public properties of the object once it’s instantiate.

The variable “shy” is scoped within MyObj, so it functions as a private variable. The functions “arbitraryHandler” and “publicFunction” can both access “shy” but shy cannot be accessed from outside the function, e.g. the following would throw an error:

//instance of MyObj from the window object;
var moe = window[namespace]
alert(moe.shy); // FAIL - private variable.

Super. Now I have public and private members in JavaScript. And they said it couldn’t be done!

The OO JavaScript convention is to write a lean constructor object, and then append public methods or properties to the function’s prototype property. Like so:

MyObj.prototype.newProperty = "shiny";
MyObj.prototype.newMethod = function(parm) {alert(parm);}

Anything you add to an object’s prototype is available to all instances of that object.

But here’s where I got off the rails. Remember my private member “shy”? The variable “shy” isn’t a private member in the same way that a member is private in a classical language like Java or C#.

It only acts private because it available to the local scope of the constructor function. So when I tried something like this, it fails:

MyObj.prototype.getShy = function() {alert(shy);} //FAIL

Doh. What went wrong?

All members of objects in JavaScript are public, any variables tucked away within a function are local to that function. When I added a method to the objects prototype, the local scope of the function is not available (and to be double plus specific, I added a method to the object’s prototype which in reality is the object which my object is based upon or inherits from; Object in this case; so I was really adding a public method to an entirely different object)

To summarize, if you’re writing OO JavaScript, and you want to store some private values accessible by public methods - place both your private values and your public methods in the same scope, i.e. the constructor function.

This also opens up a neat trick called a ‘lexical closure’ which can be very handy when writing complex event handlers or callback functions — which I’ll write about next time.

« Previous | Next »


Leave a comment.
Trackback.