"Michael Winter" <M.******@blueyonder.co.invalid> wrote in message
news:op**************@news-text.blueyonder.co.uk...
<snip>
Someone else in this group, Richard Cornford, created a nice
little script that calls a function at certain intervals,
but I don't know how applicable it will be to your problem
(I don't know if it supports arguments to that function).
It probably would not be appropriate as it is designed for animation (or
shedding over a short period), which is not that task here. It was not
designed to execute functions that need arguments but the functions it
executes can be inner functions (forming closures).
The solution I was working on was never finished, and would
probably need a complete re-write any way. He would probably
be in the best position to help you.
<snip>
There is nothing like encouraging me to write a long post on a
relatively complex subject when I am supposed to be working on the FAQ
;-) I will but in return I will pass back to you the responsibility for
jumping on the OP's - eval - abuse and explaining the value of block
indentation in source code (and especially posted code).
Given the OP's starting point of:-
|function imgOff(menu, num){
| if(document.images){
| document.images["mt"+menu].src = eval("mt" +menu+ ".src");
| }
| alert("imgOff_hidemenu");
| hideMenu=setTimeout('Hide(menu,num)',500);
|}
- the possible solution might be fairly simple depending on the type
of - num -. If it is a number (or a string representation of a number)
then it might just be a matter of building the string argument to
setTimeout to include the values of - num - as a number literal and -
menu - as a (quoted) string literal:-
hideMenu=setTimeout('Hide((\"'+menu+'\", '+num+');'),500);
String concatenation is not necessarily implemented to be that fast and
the more such operations, and the more often they are performed the less
efficient the result. An alternative approach is to put the parts of the
string that is to be built into an Array in a suitable containing scope
(often, but not necessarily, global), assigning the variable parts of
the string to be constructed to "placeholder" elements in the Array and
then calling the Array.prototype.join method (with an empty string
argument) to output the constructed string:-
var hideParts = [ //define Array in a containing (global?) scope.
'Hide(\"',
'', // index 1, string literal
'\", ',
'', // index 3, number
');'
];
- then in the function:-
hideParts[1] = menu;
hideParts[3] = num;
hideMenu=setTimeout((hideParts.join('')),500);
But in the case of this operation it would probably not worth the effort
unless the - imgOff - function was expected to be executed excessively.
It is a technique that is more suited to more complex string
construction.
Another simple approach to passing variables to setTimeout (that can
work with any type of value) is to assign the required values to global
variables and place the names of those variables in the setTimeout
string argument:-
var globalMenu;
var globalNum;
function imgOff(menu, num){
if(document.images){
document.images["mt"+menu].src = this["mt"+menu].src;
}
globalMenu = menu;
globalNum = num;
hideMenu=setTimeout('Hide(globalMenu, globalNum)',500);
}
Obviously that approach has to be handled with extreme caution because
there is only one set of global variables so there will be concurrency
issues if more than one setTimeout is initiated with the same string
argument within their timeout period.
In addition to accepting strings as its first argument, setTimeout will
accept a function reference as its first argument (at least on the more
modern browsers). Some of those setTimeout implementations will
additionally accept extra arguments (beyond the milliseconds value in
the second argument) when the first argument is a function reference.
Those additional arguments can be used to pass parameters in the call to
setTimeout:-
hideMenu=setTimeout(Hide, 500, menu, num);
Unfortunately this is the worst supported version of setTimeout. So much
so that it is just unsuitable for anything but Intranet use.
However, the version of setTimeout that accepts function references as
its first argument (but without the 3rd+ arguments) is relatively well
supported these days and may work on compact profile (ECMA 327)
implementations that take advantage of the option not to implement the -
eval - function (which is internally required to process string
arguments). It is also more efficient than the string argument only
version of setTimeout because it can execute the function without having
to interpret a string as source code. Finally the function reference
version of setTimeout can be exploited, along with javascript's ability
to form closures, to provide another means of passing arguments with
setTimeout.
There is no point trying to explain this without saying something about
closures, which will need some background: When a function is called the
implementation enters something called an "execution context"
(implementation details are not specified) and that execution context is
provided with an internal property known as the "variable" object (by
ECMA 262). Each call to a function creates a new execution context and
each execution context has its own variable object. The variable object
is used to store the values of the function's formal parameters the
function's inner function declarations and the function's local
variables, all as named properties.
When the code within a function body attempts to resolve an identifier
(or the leftmost identifier in a property accessor) the first place it
looks for a value with the corresponding name is on the variable object
in its execution context, and there it would find the function local
variables, inner function declarations and the function's formal
parameters (if not found the implementation may (subject to the
following notes) then set off down the scope chain looking for
properties with that name).
One of the features of javascript is that it allows inner functions;
functions that are declared within other function or function
expressions that occur within the code of a function body. Although ECMA
262 allows function object to be shared whenever it would not be
possible to distinguish between two function objects, experimentation
suggests that no current implementation have take advantage of this
possibility and as a result all inner function are created as distinct
function objects, either during the second stage of the initialisation
of the variable object when inner function declarations are resolved, or
inline as function expressions are evaluated. So inner function objects
can be considered to exist within the execution context and separate
executions of an outer function would create different function objects
for its inner functions. (But even if sharing were implemented any
shared functions would have to behave as if they were distinct to the
execution context. Which probably explains why this has apparently never
been implemented.)
Inner functions are allowed direct access to the parameters, local
variables and declared inner functions of their outer function.
Identifier resolution starts with the variable object in their own
execution context but if the identifier is not resolved at that point
the variable object in the execution context in which they were created
is examined prior to resolution against the scope chain. Inner functions
may also be indefinitely nested so all of the variable objects in
containing execution contexts will be examined prior to scope chain
resolution.
(Note that within the execution context of inner functions, where the
outer function is executed as the method of an object and the - this -
keyword would represent a reference to that object, the - this - keyword
always refers to the *global* object. That is, inner functions do not
inherit the association with an object that may apply to the execution
of their outer function.)
At the end of the execution of a function the execution context and its
variable object would normally become available for garbage collection,
freeing any references to inner function objects and making them also
available for garbage collection.
However, function objects, including those created as inner functions,
are just objects and references to those functions can be assigned as
properties of other objects (either directly or returned from a function
call and assigned to an object property or local variable). If this is
done it remains possible to call the inner function after the end of the
execution of its outer function call. That inner function is still
allowed to resolve identifiers as properties of the variable object from
the execution context in which it was created. So the assignment of the
inner function object to a property/variable outside the execution
context in which it was created preserves, at minimum, the variable
object from that execution context (by holding a reference to it,
preventing the garbage collection of it). That preserved variable object
acts as a sort of "private" repository, retaining its initial values and
only exposing them to access/modification by code within function
objects created in the execution context for which the variable objects
was created. It is the relationship between the preserved variable
object of the execution context of outer function and references to
inner functions held in properties of objects outside of that execution
context that is a closure.
So, how to take advantage of that to pass arguments to setTimeout? The
possibilities are endless but in the case of the OP's original function
a simple inner function expression will do:-
function imgOff(menu, num){
if(document.images){
document.images["mt"+menu].src = this["mt"+menu].src;
}
hideMenu=setTimeout((function(){Hide(menu, num);}), 500);
}
In this case the function object that represents the inner function is
created when the function expression is evaluated. The function
expression evaluates as a reference to that function object and it is
that reference that is passed to the setTimeout function as its first
argument. setTimeout holds on to the reference to that function object
until it is time to execute it. When the function gets executed it still
has access to the variable object in the execution context in which it
was created; the execution of - imgOff - that initially made the call to
setTimout. That variable object holds named properties corresponding
with the formal parameters - munu - and - num -, so when the inner
function uses those identifiers to pass arguments to - Hide - it is the
values that were passed to - imgOff - when it was called that are passed
on to - Hide -.
Once the inner function has been executed by setTimeout there will no
longer be any reference to it and so it becomes available for garbage
collection, freeing its reference to the variable object form the
execution context in which it was created and allowing that object to
also be garbage collected.
If the - imgOff - function was big and had a lot of parameters and local
variables then the closure formed would be relatively large and might
represent an unreasonable burden on the system. An alternative approach
to using a closure to pass parameter to setTimeout would be to create a
smaller (and potentially more general) function specifically for the
task:-
function callWithSetTimeout(funcRef, a, b, c, d, e, f, g){
/* Return a reference to the inner function created with
the function expression:
*/
return (function(){
/* Call the function passed by reference as the first
argument to the outer function and pass all of the
7 remaining parameters to it as its arguments:
*/
funcRef(a, b, c, d, e, f, g);
});
}
function imgOff(menu, num){
if(document.images){
document.images["mt"+menu].src = this["mt"+menu].src;
}
hideMenu=setTimeout(callWithSetTimeout(Hide,menu, num), 500);
}
Now the - imgOff - function calls the - callWithSetTimeout - function,
passing a reference to - Hide - as the first argument and - menu - and -
num - as the second ant third arguments. Then - callWithSetTimeout -
returns a reference to its inner function (created with an anonymous
function expression) and it is that function reference that is passed on
as the first argument to setTimeout.
When the inner function of - callWithSetTimeout - is called it executes
the - Hide - function, which is referred to by its - funcRef -
parameter, and passes all of its other parameters to - Hide - as its
arguments. It shouldn't matter that only - a - and - b - have had values
assigned as javascript functions do not care how many arguments are
passed when they are called or whether those arguments are undefined.
However, if - Hide - acts directly on its - arguments - property then
this implementation of - callWithSetTimeout - would not be appropriate.
The advantage of this type of approach is that it is more general and
could be directly employed by other functions, either with or without
setTimeout calls.
Using setTimeout with a function reference as its first argument works
on modern browsers but IE 4 and Opera 5, for example, only support
string arguments. Using closures to allow function references to be used
where supported and transparently fall-back to string arguments was the
subject of one of the branches in the thread: "closures, what are they
good for?" from 2003-04-22:-
<URL:
http://groups.google.com/groups?thre...8300dec7%40new
s.demon.co.uk >
(will wrap)
Richard.