1

How to alter the JavaScript code below so that it can avoid exposing the variables and functions to the global scope?

var nMax = 10;
var i = 0;
var step = function(){
                //do stuff
                i += 1;
                if(i < nMax){
                                step();
                }else{
                                alert('finished');
                }
}
step();

Ideally it would be grateful if the reason behind it could be provided.

Any idea would be very much appreciated!

Pingpong
  • 7,681
  • 21
  • 83
  • 209

4 Answers4

4

Just wrap it in an anonymous function, and call that function immediately:

(function(){
    var nMax = 10;
    var i = 0;
    var step = function(){
                    //do stuff
                    i += 1;
                    if(i < nMax){
                                    step();
                    }else{
                                    alert('finished');
                    }
    }
    step();
})();

Another Example: http://jsfiddle.net/n5Srd/

Paul
  • 139,544
  • 27
  • 275
  • 264
  • D'oh. Beat me by *six seconds*. Actually, my way exposes the function name `step`, which I thought was what the OP wanted. YMMV. – Michael Lorton Aug 16 '11 at 01:55
  • This way I cannot use `step` function outside of the anonymous function. – ShankarSangoli Aug 16 '11 at 01:58
  • Haha yeah I was quick on this one :) He asked for how to not expose the function as well. – Paul Aug 16 '11 at 01:58
  • @Shankar That's right, That's what the OP wants. For `step`, `nMax` and `i` to not be exposed to the outer scope. – Paul Aug 16 '11 at 01:59
  • Either way, it's better to use a function declaration (as in my answer) than a function expression (as in the OP, this answer, and Malvolio's answer). Why? http://stackoverflow.com/questions/1013385 – Matt Ball Aug 16 '11 at 02:00
  • @Matt Ball I don't see anything in that answer about why function declarations are better. As long he stores the function in step before calling it there is nothing wrong with a function expression. – Paul Aug 16 '11 at 02:05
  • The only difference I see (that's remotely relevant) is that declarations are "hoisted" (evaluated on load, regardless of where they are in the file), which strikes me as a bad thing. @Matt Ball -- can you modify your answer to explain specifically why you think declarations are better (and how your "cleaned up" version of PP's answer differs *at all* from the original). Thanks. – Michael Lorton Aug 16 '11 at 03:12
2

The standard way would be

var step = function(){
  var nMax = 10;
  var i = 0;
  return function() {
                //do stuff
                i += 1;
                if(i < nMax){
                                step();
                }else{
                                alert('finished');
                }
  };
}();
step();
Michael Lorton
  • 43,060
  • 26
  • 103
  • 144
1

An alternative to using a closure: functions are objects, so you can attach values to them just like any other object:

function step()
{
    step.i++;

    if (step.i < step.nMax) step();
    else alert('finished');
}

step();

Or, use an object to namespace the function and variables:

var stepper = 
{
    i: 0,
    nMax: 10,
    step: function ()
    {
        this.i++;

        if (this.i < this.nMax) this.step();
        else alert('finished');
    }
};

stepper.step();

And here's a cleaner version of @PaulPRO's answer which uses a function declaration rather than a function expression:

(function ()
{
    var i = 0,
        nMax = 10;

    function step()
    {
        i++;

        if (i < nMax) step();
        else alert('finished');
    }

    step();
})();
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • True (+1). However, there is only *one* "step" function-object. Thus, the behavior may or may not be as desired... also can use `arguments.callee` to avoid specifying `step`... –  Aug 16 '11 at 01:58
  • You're right (and probably already know what I'm about to say) but [`arguments.callee` should be avoided since there is a better, more efficient, simple alternative](https://developer.mozilla.org/en/JavaScript/Reference/Functions_and_function_scope/arguments/callee). – Matt Ball Aug 16 '11 at 02:03
  • This doesn't solve the OP's problem of hiding the function from the outer scope. If he already has a function called step, or a variable named stepper it will conflict – Paul Aug 16 '11 at 02:05
  • @Paul I took a second look at your code and saw that it will actually work - I mistook it for a slightly different scenario, apologies. – Matt Ball Aug 16 '11 at 02:17
  • 1
    @Matt Ah, I see. No worries :) – Paul Aug 16 '11 at 02:21
  • @Matt Ball The problem with just "step" here is that it is not closured in the first example. Thus it is more prone to lead to erroneous behavior, one function-object aside. Also, `arguments.callee` allows use of a truly anonymous recursive function ;-) –  Aug 16 '11 at 02:23
  • @pst And just for fun, here is a function that computes an integers factorial recursively entire anonymously and without arguments.callee: http://jsfiddle.net/Paulpro/Cnd9n/ It works by passing an anonymous copy of itself as an argument to itself so that it can call itself on itself again. :) See what I meat by fun ;P (Not practical in the slightest lol) – Paul Aug 16 '11 at 06:12
0

Put in an object so fn gets called via that:-

 var stepHolder = {};
 stepHolder.step = (function(nMax){
 var i = 0;
 return function step(){
            //do stuff
             i += 1;
            if(i < nMax){
                            step();
            }else{
                            alert('finished');
            }
  };}
  )(10);

    stepHolder.step();
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
QuentinUK
  • 2,997
  • 21
  • 20