Albert
/*====================================================
Timer.js
====================================================*/
function Timer(_sprite, _interval) {
var sprite = _sprite;
var interval = _interval;
var isRunning = false;
var timeoutID = false;
this.Clock = function() { // called after setTimout interval
if (isRunning) {
isRunning = sprite.OnClockTick();
if (isRunning)
timeoutID = window.setTimeout("this.Clock()", interval);
else
this.Stop();
}
}
this.Stop = function() {
if (timeoutID) {
window.clearTimeout(timeoutID);
timeoutID = false;
};
isRunning = false;
}
this.SetInterval = function(_interval) {
this.Stop();
interval = _interval;
isRunning = true;
timeoutID = window.setTimeout("this.Clock()",interval);
}
this.Continue = function() {
if (!isRunning) {
isRunning = true;
timeoutID = window.setTimeout("this.Clock()",interval);
}
}
}
--
Don't you see that the whole aim of Newspeak is to narrow the range of
thought? In the end we shall make thoughtcrime literally impossible,
because there will be no words in which to express it.
-- George Orwell, 1984
Never mind. I removed "this." from Clock and setTimeout(Clock,
interval) and this works. I just got my copy of O'Reilly's book
"JavaScript - The Definitive Guide (4th Ed.)" and they had an example;
so it's already begun to pay for itself :-)
Clock = function() { // called after setTimout interval
if (isRunning) {
isRunning = sprite.OnClockTick();
if (isRunning)
timeoutID = window.setTimeout(Clock, interval);
else
this.Stop();
}
}
<snip>
Obviously O'Reilly did not read this newsgroup for long enough before
printing that, or you missed what they should also have written. The
comment in the code is AFAICS absolutely accurate. But, if
interval=1000, the function will not be started once every second. The
average interval will be a little over 1027 ms in Win98, and a little
over 1005 ms in XP, IIRC.
Via the newsgroup FAQ :
<URL:http://www.merlyn.demon.co.uk/js-date1.htm#TaI> refers.
<URL:http://www.merlyn.demon.co.uk/js-date3.htm#RC> shows it fixed.
--
© John Stockton, Surrey, UK. ?@merlyn.demon.co.uk Turnpike v4.00 IE 4 ©
<URL:http://jibbering.com/faq/> Jim Ley's FAQ for news:comp.lang.javascript
<URL:http://www.merlyn.demon.co.uk/js-index.htm> JS maths, dates, sources.
<URL:http://www.merlyn.demon.co.uk/> TP/BP/Delphi/JS/&c., FAQ topics, links.
Thanks for the reply, John. The code was mine, not O'Reilly's. Their
example(s) in the book simply gave me a clue as to how to fix my code.
Actually, for my purposes, it still doesn't work. I was attempting to
have separate timers for each sprite; Clock is a global, and therefore
with two timers I had one instance of Clock overriding the other. I am
refactoring the code for a different solution now, using setInterval
rather than setTimeout.
String arguments passed to setTimeout/Interval are evaluated in the
global context.
Function reference arguments are passed as a reference to a function and
so property accessors that refer to a function as a property of an
object will not be executed as a method of the object but just a
function executing in the global context. The exception being inner
functions which will be executed in the global context (this == the
global object) but as inner functions they will still be able to
reference local variables and parameters of their outer function(s if
nested).
The passing of references to inner functions is the easiest method of
associating an argument to setTimeout/Interval with an object instance:-
MyObject.prototype.exampeFunc(){
var self = this;
function innerFunction(){
if(self.anyProperty){
self.anyMethod();
}
}
setTimeout(innerFunction, 1000);
//innerFunction (no parenthesises on function reference)
//is called in 1000 milliseconds (ish) and can refer to the
//- this - from this execution context as - self - (the outer
//function local variable)
}
Passing function reference arguments to setTimeout/Interval is not
supported in some older browsers (IE 4 and Opera 5 at least). If that is
important then there is a workaround that involves providing the
function that is passed with a custom toString method (google search
c.l.j. for explanations and examples).
Richard.
I want to put an onresize handler onto every cell in a
table. My resizeHandler function insists on knowing
which TD a given invokation is associated with.
We should actually have a setTimeout, too, since IE is
a bit of a laggard in this arena:
for each myCell in the table we can do:
myCell.onresize = function () {
var tmpVar = this;
setTimeout(function() {resizeHandler(tmpVar)}, 20)}
This seems to be working. But I am really suspicious
of that tmpVar. Can someone explain to me whether I'm
getting lucky or whether that tmpVar is safe from being
overwritten if other cells are getting resized. I.e. what is
that tmpVar bound to?
In the interests of better understanding, I'd like to change
the scenario a bit. This time resizeHandler2 only wants to
have handed to it the explicit rowIndex and cellIndex of the
cell that's responsible for its invokation.
So one might imagine the setup proceeding as follows:
for (var i=myTable.rows.length-1;i>=0;i--)
for (var j=myTable.rows[i].cells.length-1;j>=0;j--)
myTable.rows[i].cells[j].onresize = function () {
setTimeout(function () {resizeHandler2(i, j)}, 20)}
This is incorrect, because i and j are not the current values
but rather future values and get passed in as -1 when invoked.
How can I fix this up, so that what I pass in is the i and j
at the time of the function construction and not execution
(without introducing strings. The point being that I want to
know whether I can combine the two approaches)?
Thanks,
Csaba Gabor from New York
"Richard Cornford" <ric...@litotes.demon.co.uk> wrote in message news:bkli0b$ips$1...@hercules.btinternet.com...
> for each myCell in the table we can do:
>
> myCell.onresize = function () {
> var tmpVar = this;
> setTimeout(function() {resizeHandler(tmpVar)}, 20)}
>
> This seems to be working. But I am really suspicious
> of that tmpVar. Can someone explain to me whether I'm
> getting lucky or whether that tmpVar is safe from being
> overwritten if other cells are getting resized.
It is safe.
You declare tmpVar as a local variable. That means that a new variable
is created for each call to the (anonymous) function it lives in.
The inner function expression evaluates to a *closure*. It is a function
value that includes references to all the variables that it uses.
That means that a reference to the local variable survives longer than
the call to the function it lives in. That can be confuzing to people
used to languages like C and C++, where local variables are created on
the stack and removed when the function returns. In this case, it is
just what you need!
> I.e. what is that tmpVar bound to?
For each call, it is bound to a new location, and the function inside
is also new each time.
> In the interests of better understanding, I'd like to change
> the scenario a bit. This time resizeHandler2 only wants to
> have handed to it the explicit rowIndex and cellIndex of the
> cell that's responsible for its invokation.
>
> So one might imagine the setup proceeding as follows:
> for (var i=myTable.rows.length-1;i>=0;i--)
> for (var j=myTable.rows[i].cells.length-1;j>=0;j--)
> myTable.rows[i].cells[j].onresize = function () {
> setTimeout(function () {resizeHandler2(i, j)}, 20)}
>
> This is incorrect, because i and j are not the current values
> but rather future values and get passed in as -1 when invoked.
Correct. The inner function contains references to the same instances
of the variables i and j. You can see a variable "x" as a way to
reference a *location*, and that location can contain a *value*.
The function closure remembers the association from variable name
to location, not the value currently in that location. The value
is only looked up when the code containing the variable is executed.
> How can I fix this up, so that what I pass in is the i and j
> at the time of the function construction and not execution
> (without introducing strings. The point being that I want to
> know whether I can combine the two approaches)?
You need to create new locations for each inner function. The simplest
(and perhaps the only) way is to put it inside a function. Chang the
last line to:
setTimeout(
function(i,j) {return function () {resizeHandler2(i, j);};}(i,j),
20)}
By wrapping the innner function in another function, we make the i and
j be local variables that are unique to each call. The arguments are
passed as values, so the new variables have the current *values* of
the outer i and j.
We need not call the new variables i and j, they could be called anything:
function(foo,bar) {return function () {resizeHandler2(foo,bar);};}(i,j)
and we could create them with "var" instead of as arguments:
function() {var x=i,y=j;return function (){resizeHandler2(x,y);};}()
The outer function is called immediately, so the code is executed. The
new local variables gets the *values* of i and j. The reference to
these new variables are kept in the returned function.
Behind all of this is the notion of a "lvalue", short for "left value".
How do you evaluate a variable. What is the meaning of "x" in a program.
There are two answers, depending on where "x" occurs, whether it is on
the left or right side of an assignment.
When you write an assignment
x = y;
we have two variables. The one on the left hand side of the assignment
is the variable having its value changed. What happens is:
Find value of right hand side:
Look up "y" in the variable list to find the location it refers to.
Find the value stored in that location.
Look up "x" in the variable list to find the location it refers to.
Store the value from before in that location.
The right hand side is evaluted for its value. The left hand side is
evaluted for its location (it's "l-value"), not its right-value (which
is what we normally call its *value*).
Closures retain the lvalue of a variable.
Hope this helps.
/L
--
Lasse Reichstein Nielsen - l...@hotpop.com
Art D'HTML: <URL:http://www.infimum.dk/HTML/randomArtSplit.html>
'Faith without judgement merely degrades the spirit divine.'
thank you, Thank You, THANK YOU for a super fantastic
post. I had not thought about this idea of returning a function
from a function. Very nice. I learned so much about this new
way (for me) of looking at these calls. Thanks again for making
things so clear.
Deep regards from New York,
Csaba
Can you call what you did in your example a functor?
I had never thought of doing functors in javascript.
Thanks Lasse! Thanks Csaba2000!
> Can you call what you did in your example a functor?
That depends what you mean by "functor". It is not a functor in the
sense of Standard-ML. There a functor is a mapping from a structure to
a structure (where a structure is a name space/module).
This is just a function that returns a function, a simple higher order
function.