0
\$\begingroup\$

I was writing a program that used a bit of recursion with objects, and it wasn't working at all. After debugging I realized that JS's evil scope was playing nasty tricks.

To spare you guys a wall of text code, I wrote a smaller program:

//Class definition and framework:
TestClass=function(label,level){
    this.label=label;
    this.level=level;

}

TestClass.prototype.recur=function(){
   r=Math.random(); //initialise a local variable
   document.write(this.label+":"+r+"<BR/>");
   if(this.level==0){return;} //exit after this if level==0, we don't want to blow up the call stack
   x=new TestClass("inner1",0)
   x.recur();
   document.write(this.label+":"+r+"<BR/>"); //Check if r has changed
   y=new TestClass("inner2",0)
   y.recur(); 
   document.write(this.label+":"+r+"<BR/>"); //check if r has changed
}

//Run this:
a=new TestClass("outer",1);
a.recur();

Output:

outer:0.5542382076382637
inner1:0.7689833084587008
outer:0.7689833084587008
inner2:0.24465859192423522
outer:0.24465859192423522

As one can see, the value of r in the outer function has changed. From this, I see that JS joins the scope of a function with a function (recursively) called in itself. This seems slightly icky and illogical.

My own solution to this was to make every locally initialized variable a member variable. In this example, I would replace all instances of r with this.r.

Then, since I like my objects to be clean, I wrote a garbage collector. Final code would be somewhat like:

//Class definition and framework:
TestClass=function(label,level){
    this.label=label;
    this.level=level;

}
TestClass.allowedProperties=["label","level"] // do NOT blow these up during garbage collection
TestClass.prototype.recur=function(){
   this.r=Math.random(); //initialise a local variable
   document.write(this.label+":"+this.r+"<BR/>");
   if(this.level==0){return;} //exit after this if level==0, we don't want to blow up the call stack
   x=new TestClass("inner1",0)
   x.recur();
   document.write(this.label+":"+this.r+"<BR/>"); //Check if r has changed
   y=new TestClass("inner2",0)
   y.recur(); 
   document.write(this.label+":"+this.r+"<BR/>"); //check if r has changed
}

//Garbage collector:
TestClass.prototype.cleanup=function(){
    for(x in this){
        if(TestClass.allowedProperties.indexOf(x)==-1){
            delete this[x]; 
        }
    }
}


//Run this:
a=new TestClass("outer",1);
a.recur();
a.cleanup();

This manages the job reasonably well, and cleans up after itself. But I feel that this is a rather inelegant way to do it. Is there a better, elegant way to deal with this form of evil javascript scoping?

\$\endgroup\$
1
  • 2
    \$\begingroup\$ x = foo is global, var x = foo is local \$\endgroup\$ – Raynos Apr 14 '12 at 7:27
1
\$\begingroup\$

"From this, I see that JS joins the scope of a function with a function (recursively) called in itself."

No, it doesn't. You created a global variable called "r", not a method variable, and so there is only one "r", and it is accessible to all your JavaScript code.

Thou shalt always use var to declare a variable.

\$\endgroup\$
3
  • \$\begingroup\$ Strange. I'm self-taught, so this may have slipped me... Though I distinctly remember var global-ifying stuff. :/ \$\endgroup\$ – Manishearth Apr 15 '12 at 8:23
  • \$\begingroup\$ Wikipedia's ECMAScript_syntax article has a good description of JavaScript's scoping issues. There are several things that are non-intuitive, and yes, depending on where you place the var statement, it can create a global variable. \$\endgroup\$ – Ross Patterson Apr 15 '12 at 12:29
  • \$\begingroup\$ Ooh thanks! I've always been wary of JS scope. Scared of it more like ;-) \$\endgroup\$ – Manishearth Apr 15 '12 at 14:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.