Closure vs Rewrite

J

Jorge

What do you think about this ?

Quoting from that page:
| I proposed an alternative to him [Crockford] which looked like this,
| again, minus the alert and validation:
|
| var showNumber_Rewrite=function(i){
|   alert('This is called when it is needed, but only once');
|   var n=['Zero','One','Two','Three','Four','Five','Six',...];
|   showNumber_Rewrite=function(i){
|     if(!(i in n))return 'Invalid Number';
|     return n;
|   };
|   return showNumber_Rewrite(i);
| }
|
| The difference is this does not execute at all until it is actually
| needed. The first time it is called, the array is created and the
| function itself is rewritten, creating a closure, such that each
| subsequent call just validates and returns the string. Unlike his
| example, if you never need it, it never executes.

This isn't new; see here for more examples:http://peter.michaux.ca/articles/lazy-function-definition-pattern

| He didn’t like it. He couldn’t give me a real reason.

Sounds like Crockford alright.

It's a useful pattern, especially when you want to return different
versions of a function, but need to wait for the first call before you
decide which version to use. One example where this is useful is when
you load the function in the document head, but need access to
document.body.

I'm not so sure about the mentioned performance benefit; I'd need to see
(or do) benchmarks before I use this just to save "a few cycles".

By the way, the "validation" added by the author uses "i in n" to check
for the existence of array elements, which is a bad idea for obvious
reasons:

  showNumber_Rewrite("join");

There are also a few gotchas with this approach. One is the possibility
of memory leaks (see the comments on Peters blog). And if you use
something like the following, it will rewrite the function on every call:

  var myAlias = showNumber_Rewrite;
  myAlias(2);
  myAlias(2);
  myAlias(2);

This will show three alerts. You could avoid that by storing the result
of the expensive calculations in arguments.callee.cached, and have the
function return this value if it exists.

  - Conrad


Conrad, Richard, thanks.

They are both, in essence, the same pattern. Only that the "final" f()
gets assigned at different points in time, isn't it ?
 
D

Douglas Crockford

Conrad said:

Quoting from that page:
| I proposed an alternative to him [Crockford] which looked like this,
| again, minus the alert and validation:
|
| var showNumber_Rewrite=function(i){
| alert('This is called when it is needed, but only once');
| var n=['Zero','One','Two','Three','Four','Five','Six',...];
| showNumber_Rewrite=function(i){
| if(!(i in n))return 'Invalid Number';
| return n;
| };
| return showNumber_Rewrite(i);
| }
|
| The difference is this does not execute at all until it is actually
| needed. The first time it is called, the array is created and the
| function itself is rewritten, creating a closure, such that each
| subsequent call just validates and returns the string. Unlike his
| example, if you never need it, it never executes.

This isn't new; see here for more examples:
http://peter.michaux.ca/articles/lazy-function-definition-pattern

| He didn’t like it. He couldn’t give me a real reason.

Sounds like Crockford alright.


You are a rude little man.

The real reason is that the performance benefit of this technique is the saving
of one if statement per invocation. I think that is too small a payoff to
justify this level of trickiness.

I prefer keeping code as readable as possible, resorting to trickery only when
there is overwhelming justification. This technique appears to me to be unjustified.

Other than that, it's great!
 
T

Thomas 'PointedEars' Lahn

Conrad said:
It's overkill in the showNumber example, I agree. There are other
situations where it could actually be useful; for example measuring the
browser's viewport area. Such a function could be called a number of
times in short succession if it's used in an onresize handler. Rewriting
the function, or (better) storing the appropriate sub-function as a
property of arguments.callee, could lead to a measurable performance
difference there.

Please forgive my ignorance: How?


PointedEars
 
L

Lasse Reichstein Nielsen

Conrad Lender said:
It's overkill in the showNumber example, I agree. There are other
situations where it could actually be useful; for example measuring the
browser's viewport area. Such a function could be called a number of
times in short succession if it's used in an onresize handler. Rewriting
the function, or (better) storing the appropriate sub-function as a
property of arguments.callee, could lead to a measurable performance
difference there.

If you are going for performance, I wouldn't use arguments.callee (or
a global variable for that matter) to access the function.
Using arguments.callee is likely to trigger creation of the arguments
object, which an optimized JS implementation would otherwise avoid if
the "arguments" variable isn't used. It will cause a slowdown on each
call to the function.

Reading a property of the global object might trigger security checks
that are also costly. An enclosing scope that is not the global scope
seems like a better location.
But there is no need for a lazy computation in this case. It's simple
to detect the correct version and install it, before registering the
resize handler. Whatever you save if the user never resizes the page
is a small one-time cost.

/L
 
T

Thomas 'PointedEars' Lahn

Lasse said:
If you are going for performance, I wouldn't use arguments.callee (or
a global variable for that matter) to access the function.
Using arguments.callee is likely to trigger creation of the arguments
object, which an optimized JS implementation would otherwise avoid if
the "arguments" variable isn't used. It will cause a slowdown on each
call to the function.

Such an optimized "JS" implementation would not be conforming as the
ECMAScript Language Specification, Edition 3 Final, section 10.1.8, says
that "the `arguments' object" "is [to be] created and initialised" "when
control enters an execution context for function code", and _not_ when it
is first used. Your recommendation is not valid.


PointedEars
 
R

Richard Cornford

Thomas 'PointedEars' Lahn said:
Lasse said:
If you are going for performance, I wouldn't use arguments.callee
(or a global variable for that matter) to access the function.
Using arguments.callee is likely to trigger creation of the
arguments object, which an optimized JS implementation would
otherwise avoid if the "arguments" variable isn't used. It will
cause a slowdown on each call to the function.

Such an optimized "JS" implementation would not be conforming as
the ECMAScript Language Specification, Edition 3 Final, section
10.1.8, says that "the `arguments' object" "is [to be] created
and initialised" "when control enters an execution context for
function code", and _not_ when it is first used.

The problem with that is that ECMAScript implementations are only
required to 'behave as if ... ', and while the simplest way for an
implementation to behave as if it did precisely what the spec says it
should do (though I am not sure that "should do" is the right way of
stating that) is to be written so that it does do it that is not
required.

If a function makes no reference to an - arguments - object the
behaviour of the code will not be influenced at all if no - arguments -
object gets created for that execution context.

It is worth remembering that - arguments - objects have a dynamic
linkage with the Variable object's 'formal parameter' properties
(theoretically open-ended, but never implemented as such) and
facilitating that means a bit more work than simply creating a new
native ECMAScript object.

It also appears, from discussions on the ES 3.1 discussion mailing lists
between some of the people who have written some of the implementations
currently in use, that current implementations frequently don't create
arguments objects for execution contexts in which they are not
referenced.
Your recommendation is not valid.

I also would not use - arguments.callee - for this type of thing, mostly
for personal aspheric reasons rather than any direct perception of
related 'efficiency'. I am not convinced that the 'problem' that doing
so attempts to address needs to be introduced into any system.

Richard Cornford.
 
R

Richard Cornford

Conrad Lender wrote:
On the JSLint list, I've seen a number of very short replies
from you to questions or feature requests that I thought
deserved more of an explanation. Other messages didn't receive
any response at all. JSLint is such an important tool for JS
developers; it can be quite frustrating when its author appears
unresponsive to suggestions and criticism.
<snip>

Manny things deserve more/better explanations than they get, but you
should consider the position of the people (or person) being asked to
provide those explanations. Explaining the same thing a few times can be
useful. Doing so focuses your thinking and each successive explanation
can improve on the previous (in one way or another). But get to the
point of being asked for the same expiation of the 40th time, and
particularly with all the previous expiations in the public record, and
it is quite easy to see how a certain reluctance to respond may come
into play.

I recall that years ago (5 or 6) I argued with Douglas Crockford on this
group about whether JSLint should be objecting to 'fall through' in -
switch - structures. I was in favour of allowing it, JSLint had other
ideas, and no amount of talk of 'issues' caused by it was going to
convince me otherwise.

Given that I can entirely understand an unwillingness on Douglas' part
to carry on that discussion. He may well have thought 'he wants to learn
the hard way, so let him', and left it at that. I know that I often end
up thinking that when people disregard (what I think of as)
well-reasoned argument in favour of carrying on doing what they were
going to do anyway.

In that case I did learn the hard way (which is by falling over the
'issues' for myself). Experience (particularly on large-scale projects)
has convinced me that 'fall through' is dangerous and probably is best
subject to a blanket injunction. The thing is that I still could not
present a good explanation of the 'issues' beyond vague talk of
'maintenance headaches' and 'hard to trace bugs'. That is, precisely the
same talk that failed to convince me half a decade ago. Some of these
things are apparently not as easy to put into words as some may prefer.

Richard.
 
T

Thomas 'PointedEars' Lahn

Richard said:
It is worth remembering that - arguments - objects have a dynamic
linkage with the Variable object's 'formal parameter' properties
(theoretically open-ended, but never implemented as such) and
facilitating that means a bit more work than simply creating a new
native ECMAScript object.

All the more reason why it does not make sense to create the object only
when referenced. If created only when execution reached the relevant
statement, that would slow down execution. The alternative to that is to
scan the entire function body for `arguments' when control enters the
execution context, to see if the reference is there, which also is not very
efficient. Better create it always, as the Spec says.
It also appears, from discussions on the ES 3.1 discussion mailing lists
between some of the people who have written some of the implementations
currently in use, that current implementations frequently don't create
arguments objects for execution contexts in which they are not
referenced.

Apparently those people have not thought through what that would mean in a
production environment.


PointedEars
 
P

Peter Michaux

I recall that years ago (5 or 6) I argued with Douglas Crockford on this
group about whether JSLint should be objecting to 'fall through' in -
switch - structures. I was in favour of allowing it, JSLint had other
ideas, and no amount of talk of 'issues' caused by it was going to
convince me otherwise.

It is unfortunate JSLint is not programmed in a modular way so that an
abstract syntax tree is first produced and then various rules can
analyze that tree. I believe it would make it a more flexible tool for
more people's points of view.
Given that I can entirely understand an unwillingness on Douglas' part
to carry on that discussion. He may well have thought 'he wants to learn
the hard way, so let him', and left it at that.

I remember reading that Douglas learned this the hard way himself.
Something about discussing the issue with an opponent to fall through
and then Douglas himself having a bug due to fall through the next
day.

Peter
 
L

Lasse Reichstein Nielsen

Thomas 'PointedEars' Lahn said:
Lasse said:
If you are going for performance, I wouldn't use arguments.callee (or
a global variable for that matter) to access the function.
Using arguments.callee is likely to trigger creation of the arguments
object, which an optimized JS implementation would otherwise avoid if
the "arguments" variable isn't used. It will cause a slowdown on each
call to the function.

Such an optimized "JS" implementation would not be conforming as the
ECMAScript Language Specification, Edition 3 Final, section 10.1.8, says
that "the `arguments' object" "is [to be] created and initialised" "when
control enters an execution context for function code", and _not_ when it
is first used. Your recommendation is not valid.

The specification defines semantics, not implementation.
Whether you actually create the arguments object when the function is
entered, or when the arguments object is first used, is indistinguishable
and all javascript programs will behave the same in the two cases.
I.e., the semantics are identical.

Anyway, I was not saying that the arguments object was created later,
but that it is created at all. If the "arguments" variable is
definitly not used inside a function, then an implementation can
entirely avoid creating it and save time and space in doing so - still
without changing the semantics of any program.

/L 'If tree doesn't fall in the forest, and nobody is looking, does
it still take time and space?'
 
L

Lasse Reichstein Nielsen

Thomas 'PointedEars' Lahn said:
Richard Cornford wrote:
All the more reason why it does not make sense to create the object only
when referenced. If created only when execution reached the relevant
statement, that would slow down execution. The alternative to that is to
scan the entire function body for `arguments' when control enters the
execution context, to see if the reference is there, which also is not very
efficient. Better create it always, as the Spec says.

You already scan the function when you parse it, so you might as well
detect features like the use of "arguments", "with" and "eval" at that
point, before further processing the parsed function body.
Also, you only parse and compile a function once, but you might
execute a function a lot of times. That is why it makes sense to avoid
creating the arguments object, even at a slightly higher initial cost.
Apparently those people have not thought through what that would mean in a
production environment.

Do tell. What /would/ it mean in a production environment?

Again: It's an optimization that does not change the semantics of any
javascript program, but which does improve the running speed of almost
all programs.

/L
 
J

Jorge

Interesting. I've just tested this in FF2 and Opera 9.63, and I can
confirm that using the arguments object is noticably slower (about 30%
of the maximum in Opera, and 1-2 orders of magnitude in FF2). Unless I
made a mistake in my benchmark (see below).

I've put it online: <http://jorgechamorro.com/cljs/030/>

Wow, yes, arguments.callee is expensive !
 
J

Jorge

Interesting. I've just tested this in FF2 and Opera 9.63, and I can
confirm that using the arguments object is noticably slower (about 30%
of the maximum in Opera, and 1-2 orders of magnitude in FF2). Unless I
made a mistake in my benchmark (see below).


That would either mean an additional step for the user of the function,
or you couldn't place the script in the <head>, because the detection
requires access to document.body.

My 2 cents:

var func_yetAnother= function f () {
if (f.flag) { return f.x; }
f.flag= true;
return (f.x= expensive());
};

Allows returning falsy values, runs almost as fast -if not faster- and
no closures are created:

<http://jorgechamorro.com/cljs/030/>
 
T

Thomas 'PointedEars' Lahn

Lasse said:
Thomas 'PointedEars' Lahn said:
Lasse said:
If you are going for performance, I wouldn't use arguments.callee (or
a global variable for that matter) to access the function.
Using arguments.callee is likely to trigger creation of the arguments
object, which an optimized JS implementation would otherwise avoid if
the "arguments" variable isn't used. It will cause a slowdown on each
call to the function.

Such an optimized "JS" implementation would not be conforming as the
ECMAScript Language Specification, Edition 3 Final, section 10.1.8, says
that "the `arguments' object" "is [to be] created and initialised" "when
control enters an execution context for function code", and _not_ when it
is first used. Your recommendation is not valid.

The specification defines semantics, not implementation.
Whether you actually create the arguments object when the function is
entered, or when the arguments object is first used, is indistinguishable
and all javascript programs will behave the same in the two cases.

Most definitely they will not.
I.e., the semantics are identical.

They are not. It takes time to create the object, and creating it only when
execution reaches the point when it is first used, would slow down
execution.
Anyway, I was not saying that the arguments object was created later,
but that it is created at all. If the "arguments" variable is
definitly not used inside a function, then an implementation can
entirely avoid creating it and save time and space in doing so - still
without changing the semantics of any program.

Fair enough, but how can you be sure that the run time required for
additonal parsing does not compensate for the run time that might be gained
by not creating the `arguments' object when not being referred to?
/L 'If tree doesn't fall in the forest, and nobody is looking, does
it still take time and space?'

The issue here is that somebody (the script engine) always would need to
have a look if it falls (if it occurs), though. The optimization no doubt
requires more parsing than without it.


PointedEars
 
J

Jorge

They are not. It takes time to create the object, and creating it only when
execution reaches the point when it is first used, would slow down
execution.

The time to create it is === the time to create it, no matter *when*.
What matters is that if it isn't needed, why bother creating it ?
Fair enough, but how can you be sure that the run time required for
additonal parsing does not compensate for the run time that might be gained
by not creating the `arguments' object when not being referred to?

How do you know that it takes additional "parsing" time ?
How could you possibly detect while parsing a, for example, args= eval
('arg'+'uments'); ?
What if it's a call to its getter what triggers its creation
"lazily" ?
Have you ever heard about the "lazy-function-definition-pattern" ? :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,116
Messages
2,570,699
Members
47,274
Latest member
SeleneHgw8

Latest Threads

Top