The interval never stops...Can anyone explain?
Variables trapped in closure scope is the reason for that, the following code will use variable o in same scope that sets o to null.
Note: I prefer to use closure creators to limit scope and prevent other surprises. The following will create closures on they fly to keep code simple.
var o = {};
setTimeout(function(){//closure accessing o directly
console.log("why is o null here:",o);
},100);
o=null;
The following code will use o as passed o, passed o is now trapped in the created closure scope and setting o to null does not affect passed o. Mutating o (o.something=22) will affect passed o. (google "javascript by reference by value" for references)
var o = {};
setTimeout((function(o){
return function(){//closure accessing passed o
console.log("why is o not here:",o);
};
}(o)),100);
o=null;
To solve a common problem in loops creating closurs you pass the variable (i) to a function that returns a closure
for(var i = 0;i<10;i++){
setTimeout((function(i){
return function(){//not using i in the for loop but the passed one
console.log("and i is:",i);//0 to 9
};
}(i)),100);
}
Because having the i variable in the same scope as the closure will give you unwanted results:
for(var i = 0;i<10;i++){
setTimeout(function(){
console.log("and i is:",i);//10 times and i is: 10
},100);
}
Why periodic.go="" works has something to do with the pass by value by reference. How that works is shown in the following code:
function test(o){
o=22;//no mutation but an assignment
}
var o = {name:"me"};
test(o);
console.log(o);//Object { name="me" }
function test2(o){
o.name="you";//mutates
}
test2(o);
console.log(o);//Object { name="you"}
How to solve
I've changed your code a little to take advantage of protype for shared members (the go function) and create closures to make sure the scope of the closure is limited to what you actually need to be in there.
For more details read the introduction to constructor function and the this variable.
function MyInterval(){//capitalize constructor
this.name = 'Paul';
this.doContinue = true;
this.timeoutid = false;
};
MyInterval.prototype.closures ={//creates closures with limited scope
//closure for the go function setting the invoking object
go:function(me){
return function(){
console.log("In go, name is:",me.name);
me.go();
//de reference "me", we no longer need it
// can't do this in a setInterval though
me=null;
};
}
}
MyInterval.prototype.go = function(){
if(this.constructor===MyInterval){
//if you were to call go multiple times
if(this.timeoutid)
clearTimeout(this.timeoutid);
//do it again if this.doContinue is true
this.timeoutid = (this.doContinue)? setTimeout(
this.closures.go(this)
//creates a closure function
,100
):false;
return;
};
console.log("this is not the correct value:",this);
};
//added stop, good call from shadow, now you can stop immediately
// or set doContinue to false and let it run it's course
MyInterval.prototype.stop = function(){
if(this.timeoutid)
clearTimeout(this.timeoutid);
this.timeoutid=false;
};
periodic = new MyInterval();
periodic.go();
//because the clearTimeout you can accedentally do something silly like:
periodic.go();
periodic.go();
setTimeout(function(){
periodic.name='George';
}, 150);
setTimeout(function(){
periodic.name ='John';
periodic.doContinue = false;
periodic.name = 'Ringo'
}, 250);