Private Functions In JavaScript
Written by Ian Elliot   
Friday, 03 February 2012
Article Index
Private Functions In JavaScript
Closure

JavaScript doesn't have a native namespace facility so how are you supposed to keep utility and helper functions private? The solution is to use inner functions but this is just the start of the story.

A common use of JavaScript that doesn't really need the use of an object-oriented approach is where you have a single task to perform, for example make a change to a web page. In this case it is common to simply define a function that does the job and call it. 

This is fine, but if the task is even slightly complicated then you can make things easier by defining helper functions that the main function uses to get the job done. This is just classical top-down structured design, which suggests that the best way to build a program is as a hierarchy of functions.

So, for example, if we have a single main function myMain which makes use of a single helper function myHelper. The usual way to organize this is:

<script>
myMain();

function myMain(){
alert("main");
myHelper();
}
function myHelper(){
alert("Utility");
}
</script>

With the calls to alert substituting for something more interesting and complex. Also myHelper would almost certainly call other functions as would myMain so you have to think of myHelper as standing in for a long list of other functions.

This works but it has a big problem.

The problem is that myMain is a global symbol which roughly means myMain can be called from anywhere and so is myHelper. As myHelper is a function that is designed for internal use you probably don't want it to be global and hence visible everywhere. More worryingly if another library of functions that happen to be in use also defines a myHelper function we have a name collision.

To stop name collisions most languages introduce the idea of a definable namespace but JavaScript doesn't.

So what can we do to stop the pollution of the global namespace?

There are a few well known ways of creating objects and using them as private namespaces but in this case this doesn't seem like a good way to do the job.

JavaScript uses the idea of function scope to control variable visibility. Roughly speaking any variables declared in a function, anywhere in a function are local to that function. This is the mechanism we usually use to create local variables.

The same mechanism can also be used to create local or inner functions. Any function declared within  another function is local to the outer function. What this means is that the inner function can be called by the outer function but it cannot be called from the global or any other context.

For example:

myMain();
myHelper();

function myMain(){
alert("main");
myHelper();

function myHelper(){
alert("Utility");
}
}

Notice that now myHelper is defined within myMain. This means that it can be called within myMain but the call in the global level i.e. the first call to myHelper in the above program fails with an "undefined" error.

As is always the case you can define the inner function anywhere with the outer function because JavaScript automatically moves function definitions to the top of the function.

Notice that if you adopt the alternative way of defining functions:

var myHelper = function(){
alert("Utility");
}

Then you have to define the function before you use it. The reason is that variable definitions are moved to the start of the function but variable initializations are left where they are. So the variable myHelper exists from the start of the outer function but it isn't defined until the initialization statement is reached.

So the rule is you can put function myfunc(){} definitions anywhere but var myfunc=function(){} have to be placed before the function is used.

There are some other subtle points to take into account.

The first is that the inner function is local to the outer function and so created and destroyed along with it, i.e. the lifetime of the inner function is the same as the outer function.

The second is that any variable declared within the inner function are local to that function and by the usual rules cannot be accessed by the outer function.

The third is slightly more surprising. The inner function is local to the outer function and by and application of closure has access to all of the variables and functions defined within the outer function.

You can think of the outer function as behaving like a global context for the inner function. One consequence of this is that you don't have to pass important variables declared in the outer function to the inner function. You don't have to pass such variables but it is nearly always a good idea to do so.

For example:

function myMain(){
var count=0;
count++;               
myHelper();
alert(count);

function myHelper(){
alert(count);
count++;
}
}

In this case the count variable is incremented by the myHelper function and when it returns the count variable in the outer function has been changed - i.e. its value is now 2.

Passing the count variable into the function isolates it from change by the function:

function myMain(){
var count=0;
count++;               
myHelper(count);
alert(count);

function myHelper(count){
alert(count);
count++;
}
}

The only change is that count is now a parameter and the count variable in the inner function has no effect on the value of the count variable in the outer function. That is the final value of count in the outer function is 1.

So you can use inner functions to hide the functions needed to get a job done from the outside world. There is only one potential downside of this approach and this is the potential creation, destruction and then recreation of all of the inner functions each time the outer function is used. Of course if the outer function is only called once then there is no overhead. Even if the function is called more than once the overhead is likely to be small but it will vary according to the way that the JavaScript engine implements the code.

Banner

<ASIN:0470344725>

<ASIN:0596806752>

<ASIN:0470525932>

<ASIN:059680279X>



Last Updated ( Saturday, 04 February 2012 )