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?
x = foo
is global,var x = foo
is local – Raynos Apr 14 '12 at 7:27