HighDots Forums  

Re: JavaScript closure semantics

Javascript JavaScript language (comp.lang.javascript)


Discuss Re: JavaScript closure semantics in the Javascript forum.



Reply
 
Thread Tools Display Modes
  #1  
Old   
Lasse Reichstein Nielsen
 
Posts: n/a

Default Re: JavaScript closure semantics - 06-26-2003 , 09:07 AM






Dom Leonard <doml.removethis (AT) senet (DOT) andthis.com.au> writes:

Quote:
differ in their Scope chains, and when you create them inside the same
function body during one call to that function, then the scope chains
will also be identical.

Perhaps theoretically possible, but I find no evidence that either IE6
or Moz 1.3 "join" function objects as described in ECMA 262, and in
practice a new function object is created for every execution of an
inner function definition of the form
I think they do join objects in some cases, but it was not that kind
of indistinguishability I was referring to.

The two distinct functions are indistinguishable *as functions*
(extensionally). There is no way the code in the body of the function
alone can act different - it refers to the same variables and contain
the same code, even though the function objects might not compare
equal with == or ===.

When calling the functions with identical arguments, there are only
two things that differ during execution of the function body: The
value of the "this" keyword (if they are methods of different objects)
and the value of arguments.callee (the function object itself).

To make the functions act differently, you must use one of these.
Either treat them like objects (adding properties to the function
object) or using that they are methods of different objects (attaching
information to the "this"-object).


Joining of functions (which I hadn't read in detail before) is
actually interesting.

Compare
---
var x=[];
for (var i=0;i<2;i++){
function foo(){alert(i);};
x[i]=foo;
}
x[0]==x[1]
---
and
---
var x=[];
for (var i=0;i<2;i++){
var foo = function(){alert(i);};
x[i]=foo;
}
x[0]==x[1]
---
The two does act differently in some browsers (meaning that
var foo = function(){}
and
function foo(){}
are not interchangable in all cases).

In Netscape 4, both give false.
In Mozilla, both give true.
In IE6 and Opera 5+, the first gives true and the second false.

All are legal, since it is at the implementation's discretion whether
to create joined objects or not.

/L
--
Lasse Reichstein Nielsen - lrn (AT) hotpop (DOT) com
Art D'HTML: <URL:http://www.infimum.dk/HTML/randomArtSplit.html>
'Faith without judgement merely degrades the spirit divine.'


Reply With Quote
  #2  
Old   
Richard Cornford
 
Posts: n/a

Default Re: JavaScript closure semantics - 06-27-2003 , 06:48 PM






"Lasse Reichstein Nielsen" <lrn (AT) hotpop (DOT) com> wrote

<snip>
Quote:
Joining of functions (which I hadn't read in detail
before) is actually interesting.

Compare
---
var x=[];
for (var i=0;i<2;i++){
function foo(){alert(i);};
x[i]=foo;
}
x[0]==x[1]
---
and
---
var x=[];
for (var i=0;i<2;i++){
var foo = function(){alert(i);};
x[i]=foo;
}
x[0]==x[1]
---
The two does act differently in some browsers
(meaning that
var foo = function(){}
and
function foo(){}
are not interchangable in all cases).

In Netscape 4, both give false.
In Mozilla, both give true.
In IE6 and Opera 5+, the first gives true and the second false.
I was interested in your results from these tests but I was unable to
reproduce them, particularly your results for Gecko browsers. The
following are my test functions and results:-

function funcTest1(){
var x=[];
for (var i=0;i<2;i++){
function foo(){alert(i);};
x[i] = foo;
}
return (x[0]==x[1]);
}

An inner function declaration within the - for - loop.
Mozilla 1.0, 1.2, 1.3, Netscape 7.02 and K-Meleon 0.7 (Gecko browsers)
== false
Opera 6.05 & 7.11and IE 6 == true
Netscape 4.79 == false


function funcTest3(){
var x=[];
for (var i=0;i<2;i++){
x[i] = function(){alert(i);};
}
return (x[0]==x[1]);
}

An inner function expression within the - for - loop. I would not
expect this to be considered to be the same function on each iteration
of the loop.
Mozilla 1.0, 1.2, 1.3, Netscape 7.02 and K-Meleon 0.7 (Gecko browsers)
== false
Opera 6.05 & 7.11and IE 6 == false
Netscape 4.79 == false


function funcTest5(){
var x=[];
function foo(){alert(i);};
for (var i=0;i<2;i++){
x[i] = foo;
}
return (x[0]==x[1]);
}

An inner function declaration outside of the - for - loop. This I was
expecting to always be considered as the same function (ECMA 262 section
10.1.3).
Mozilla 1.0, 1.2, 1.3, Netscape 7.02 and K-Meleon 0.7 (Gecko browsers)
== true
Opera 6.05 & 7.11and IE 6 == true
Netscape 4.79 == true

There seems to be a discrepancy in the interpretation of the first
function. The inner function declaration looks like it might be being
interpreted as a function expression with optional identifier by Net 4
and Gecko browsers, but even then the results do not conform with my
interpretation of ECMA section 10.1.3 and 13.

I did repeat these test replacing - alert(i); - with - alert("m"); - to
see if the inner function's use of a local variable from it's containing
function was influencing the results. However, that set of tests
produced exactly the same results as above.

Quote:
All are legal, since it is at the implementation's discretion
whether to create joined objects or not.
I also performed an additional test to gauge the extent to which
function objects would be joined or reused. Following the examples in
the footnote to ECMA 262 section 13.2:-

function getFoo(){
function foo(){alert('m');};
return foo;
}
function funcTest9(){
var x=[];
for (var i=0;i<2;i++){
x[i] = getFoo();
}
return (x[0]==x[1]);
}

- all of the browsers used for the previous test returned false from -
funcTest9 -.

As the - foo - function returned from each call to - getFoo - should be
"indistinguishable" from one and other -

<quote cite="ECMA 262 section 13.2">
.. . . the differences between their [[Scope]] properties are not
observable, . . .
</quote>

- I would have to conclude that none of these browsers have chosen to
implement the optional optimisation mentioned in ECMA 262 section 13.2
(and 13.1.2) and that no joining of function objects is going on.

Having apparently ascertained that any inner function returned by a
function will be a unique function object and that function expressions
produce unique function objects I think that this has implications for
how inner functions should be used. For example:-

function MyObject(x){
this.x = x;
this.getX = function(){
return this.x
};
}

- will create a new function object for each MyObject created, while:-

function MyObject(x){
this.x = x;
}
MyObject.prototype.getX = function(){
return this.x
}

- should share the same function object between all instances of
MyObject.

The same would go for assigning an event handler with an inner function
expression. One function object for each invocation of the outer
function. While defining the handler function externally and assigning
it by reference would allow the same function object to be shared by
many event handlers.

If the browsers cannot or will not recognise when two functions are
indistinguishable and optimise their use the onus falls on the script
author to do so. Saving memory consumption and improving execution speed
(as less new function objects need to be created).

Leaving inner functions to be used only when their special
characteristics confer some advantage, when they actually are accessing
properties of their outer functions and/or resulting closures. (Or maybe
in code that will only be executed once.)

Richard.




Reply With Quote
  #3  
Old   
Lasse Reichstein Nielsen
 
Posts: n/a

Default Re: JavaScript closure semantics - 06-27-2003 , 07:35 PM



"Richard Cornford" <Richard (AT) litotes (DOT) demon.co.uk> writes:

Quote:
I was interested in your results from these tests but I was unable to
reproduce them, particularly your results for Gecko browsers.
It seems the context is important too. I evaluated the expressions
using eval (in my test page:
<URL:http://www.infimum.dk/HTML/javascript/jstest4.html>)
That means that the code was part of an eval block, not a function
body.

Quote:
The following are my test functions and results:-

function funcTest1(){
var x=[];
for (var i=0;i<2;i++){
function foo(){alert(i);};
x[i] = foo;
}
return (x[0]==x[1]);
}
An inner function declaration within the - for - loop.
Mozilla 1.0, 1.2, 1.3, Netscape 7.02 and K-Meleon 0.7 (Gecko browsers)
== false
Opera 6.05 & 7.11and IE 6 == true
Netscape 4.79 == false
....
Quote:
There seems to be a discrepancy in the interpretation of the first
function. The inner function declaration looks like it might be being
interpreted as a function expression with optional identifier by Net 4
and Gecko browsers, but even then the results do not conform with my
interpretation of ECMA section 10.1.3 and 13.
Indeed, it couldn't be a function expression with identifier, since
that would not declare a "foo" variable in the surroundings, and "foo"
has been declared in the next line.

I do think this is in accordance with ECMA262 section 13.2, and what
we are seeing from IE and Opera *is* the joining of the functions.

Quote:
I also performed an additional test to gauge the extent to which
function objects would be joined or reused. Following the examples in
the footnote to ECMA 262 section 13.2:-
....
- I would have to conclude that none of these browsers have chosen to
implement the optional optimisation mentioned in ECMA 262 section 13.2
(and 13.1.2) and that no joining of function objects is going on.
It is a possible optimization that is requries some testing (although
not a lot). I can understand why they haven't prioritized it (yet, I
hope).

Quote:
Having apparently ascertained that any inner function returned by a
function will be a unique function object and that function expressions
produce unique function objects I think that this has implications for
how inner functions should be used.
....
Good points, if execution speed and memory are the primary goals.

However, I am weary at designing programs for such a diverse and
evolving platform as browsers' Javascript by aiming at the artifacts
of the current impementations. I would much prefer to program for ease
of maintaining, and only optimize when the need for speed is dire.

/L
--
Lasse Reichstein Nielsen - lrn (AT) hotpop (DOT) com
Art D'HTML: <URL:http://www.infimum.dk/HTML/randomArtSplit.html>
'Faith without judgement merely degrades the spirit divine.'


Reply With Quote
  #4  
Old   
Richard Cornford
 
Posts: n/a

Default Re: JavaScript closure semantics - 06-27-2003 , 09:51 PM



"Lasse Reichstein Nielsen" <lrn (AT) hotpop (DOT) com> wrote


Quote:
I was interested in your results from these tests but I was unable
to reproduce them, particularly your results for Gecko browsers.

It seems the context is important too. I evaluated the
expressions using eval (in my test page:
URL:http://www.infimum.dk/HTML/javascript/jstest4.html>)
That means that the code was part of an eval block, not
a function body.
I will look at that page later. Now I am also going to have to reread
the ECMA Script sections on eval code and see how expected your results
were in that context.

<snip>
Quote:
... The inner function declaration looks like it might be being
interpreted as a function expression with optional identifier
by Net 4 and Gecko browsers, but even then the results do not
conform with my interpretation of ECMA section 10.1.3 and 13.

Indeed, it couldn't be a function expression with identifier,
since that would not declare a "foo" variable in the surroundings,
and "foo" has been declared in the next line.
Exactly my reason for rejecting that hypothesis.

Quote:
I do think this is in accordance with ECMA262 section 13.2, and
what we are seeing from IE and Opera *is* the joining of the
functions.
My reading of section 10.1.3 would suggest that, recognising the
function declaration as such, IE and Opera are adding a named reference
to one unique function object to the variable object and just
disregarding the fact that the declaration appears in a - for - loop, so
there is only one function object and no joining is taking place. (and
the function is not re-declared in each iteration of the loop).

My belief is encouraged by this test:-

function funcTest10(){
while(false){
function foo(){alert(i);};
}
return foo;
}
alert(funcTest10());

Where Opera and IE alert the toString version of foo and Gecko browsers
produce a JavaScript error - foo is undefined - on the - return foo; -
line.

I think it is the Gecko browsers that are off spec here (Netscape 4 can
be excused for historical reasons).

In any event, if Opera and IE were joining the functions in the previous
example why would they not implement the joining in the case that
followed the example specifically targeted for optimisation in ECMA 262
section 13.2?

<snip>
Quote:
...
Good points, if execution speed and memory are the primary
goals.
Speed an efficiency are personal obsessions. That probably stems form my
learning 68000 assembly language in the early 1990s. When you are
gauging code by the clock-cycle you get a good feeling for what is fast
and what isn't. I haven't been able to shake the impression that if code
can be faster it probably should be. I doubt that I will, it is part of
the "craft".

But JavaScript is inevitably relatively slow. Take the recent thread on
vector graphics; the library that Jim referenced uses a lot of the fast
algorithms that I implemented in assembly language for 3D vector
graphics on 8Mhz CPUs, but on a 100 times faster computer they are still
nowhere near fast enough to do 3D vector animation in a web browsers
(even the relatively crude stuff from the early 90s (though I might yet
see if I can do better)).

Memory doesn't seem that important on a desktop computer, with a 32 bit
Windows system having a 2Gb (or is it 4, I forget) memory map and using
a swap file to fake any that is not physically present. But what about
PDAs? The browser and the DOM representation take up a lot of the
available memory and don't leave that much for JavaScript to work in.

Quote:
However, I am weary at designing programs for such a diverse
and evolving platform as browsers' Javascript by aiming at
the artifacts of the current impementations. I would much
prefer to program for ease of maintaining, and only optimize
when the need for speed is dire.
I don't think that the approach that I was recommending would
necessarily compromise maintenance. Assigning public methods to a
prototype is virtually self-documenting and assigning externally defined
method to event handlers should be clear enough if they are well named
and commented.

Richard.




Reply With Quote
  #5  
Old   
Richard Cornford
 
Posts: n/a

Default Re: JavaScript closure semantics - 06-28-2003 , 08:27 AM



"Richard Cornford" <Richard (AT) litotes (DOT) demon.co.uk> wrote

<snip>
Quote:
It seems the context is important too. I evaluated the
expressions using eval (in my test page:
URL:http://www.infimum.dk/HTML/javascript/jstest4.html>)
That means that the code was part of an eval block, not
a function body.

I will look at that page later. Now I am also going to have to reread
the ECMA Script sections on eval code and see how expected your
results
were in that context.
snip

The eval call in your page is in the global context and eval processes
its input as - Program - so ECMA section 14 applies. In that case
function declarations are processed first and all of the browsers are
correct in recognising the declarations and only creating one function
object for each.

There is still a problem with Gecko browsers as:-

while(false){
function foo(){alert(i);};
}
var n = foo;

- executed inline leaves - n - holding undefined on Mozilla, etc. and a
reference to a defined function on IE and Opera. This apparent
block-scopeing of function declarations is not in line with the ECMA
specification for the treatment of Program source (section 14) or the
instantiation of the variable object (section 10.1.3).

<quote cite="ECMA 262 section 10.1.3">
.. . .
For each FunctionDeclaration in the code, in source text order, create a
property of the variable object . . . whose value is the result returned
by creating a Function object as described in 13, . . .
</quote>

"For each" does not seem to leave the option of ignoring a function
declaration just because it is within a block of code that will never be
executed and "source text order" is not code execution order. Similarly
section 14 precedes code execution with "1. Process SourceElements for
function declarations.", so if a function declaration exists its context
within inline code should not influence the creation of corresponding
function objects.

The Gecko browsers are additionally reporting the result of the function
expression as equal to the result of its previous evaluation and that
might be the result of joining the two function objects. I can certainly
see how it might be easier to recognise that two anonymous function
objects where indistinguishable in the global context than it would
within executing an function. And it does suggest that if one
optimisation has been implemented the future might hold more.

Richard.




Reply With Quote
  #6  
Old   
Dom Leonard
 
Posts: n/a

Default Re: JavaScript closure semantics - 06-28-2003 , 10:22 AM



Richard Cornford wrote:
Quote:
"Lasse Reichstein Nielsen" <lrn (AT) hotpop (DOT) com> wrote
SNIP

I am reading this with interest. I don't believe there is disagreement
in fundamentals, but it seems a good idea to identify what constitutes
acceptable closure programming, and identify what will turn out to be
browser dependent code (ugh!!@) because it relies on the *non* joining
of function objects.

<quote Lasse>
Compare
---
var x=[];
for (var i=0;i<2;i++){
function foo(){alert(i);};
x[i]=foo;
}
x[0]==x[1]
---
and
---
var x=[];
for (var i=0;i<2;i++){
var foo = function(){alert(i);};
x[i]=foo;
}
x[0]==x[1]
....
In Mozilla, both give true.
</quote>

I too was unable to reproduce this result (in an evaluator as well,
http:/dfxhtml.com/browserpad/pad.html ) and put it down to version
differences between our Mozillas - I am running 1.3. Did you test in a
later version? *Minding that* the answer if for interest. The
reliability of javascript code should be predicated on results obtaind
from any particular browser. [Aside: check the constructor value for a
function definded in the evaluator under NS4. Don't you love it ? ]

<quote [Richard]>

function funcTest10(){
while(false){
function foo(){alert(i);};
}
return foo;
}
alert(funcTest10());

Where Opera and IE alert the toString version of foo and Gecko browsers
produce a JavaScript error - foo is undefined - on the - return foo; -
line.
</quote>

In this case IE and Opera are wrong in the return value. Yes they have
done the ECMA 10.1.3 thing in initialising the activation object but
failed to spot the missing paragraph margin in ECMA-262.pdf section 13
Syntax - tsk tsk - which Mozilla did not:

<cite>
NOTE
The Identifier in a FunctionExpression can be referenced from inside the
FunctionExpression's FunctionBody to allow the function to call itself
recursively. However, unlike in a FunctionDeclaration, the Identifier in
a FunctionExpression cannot be referenced from and does not affect the
scope enclosing the FunctionExpression.
</cite>

However, this is not related to whether the browsers might or might not
have joined the foo function in a loop or across calls, and *IHMO* the
programmer who puts a named function expression in a loop is asking for
*what exactly*? What they get is my answer


To cut to the chase, both named and annonomous functions declared in a
loop end up at ECMA 262 13.2 for function object creation, where they
*may* be joined for equated grammar productions (identical source code),
or even use the *same* function object if they do not make reference to
the activation object of containing function(s).

Given that external properties of joined functions are the same, my
hypothesis is now:

H0: Browser independant code *may not* rely on obtaining a different,
unjoined function object for *any* function declared inside another
function, whether this be because of function declaration or expression
within a loop, across calls to the containing function, or whether the
function be annonomous or named. ECMA 262 both *allows* them to be
different in all cases and *allows* them to share all external
properties (including prototype and user defined properties) in all
cases (because of join operations retaining differences in the scope
chain) and in some cases even *allows* them to be the same function
object without joining.

My thoughts and observations are that javascript engines are empirically
unlikely to join (closure) function objects across multiple calls to a
container function at this time (I haven't seen any), but attribute this
to the interpretative nature of javascript (subject to optimisation at
any time) rather than the content of ECMA 262.

For courtesy, I have been offline and if thread has continued I will
certainly read. I am also probably not in your time zone - can you get
jet lag from NG

Dom





Reply With Quote
  #7  
Old   
Richard Cornford
 
Posts: n/a

Default Re: JavaScript closure semantics - 06-28-2003 , 12:30 PM



"Dom Leonard" <doml.removethis (AT) senet (DOT) andthis.com.au> wrote

<snip>
Quote:
I too was unable to reproduce this result (in an evaluator
as well, http:/dfxhtml.com/browserpad/pad.html )
I did manage to re-produce Lasse's results running his evaluating page.
I will have to look at the one above to see how it differs.

Quote:
and put it down to version differences between our Mozillas
- I am running 1.3. Did you test in a later version?
*Minding that* the answer if for interest. ...
snip

I tested with: Mozilla 1.0, 1.2, 1.3, Netscape 7.02 and K-Meleon 0.7 and
got consistent results with all. That is a good spread of Gecko versions
so I don't think that the Mozilla version is making any difference.

Quote:
quote [Richard]

function funcTest10(){
while(false){
function foo(){alert(i);};
}
return foo;
}
alert(funcTest10());

Where Opera and IE alert the toString version of foo and
Gecko browsers produce a JavaScript error - foo is
undefined - on the - return foo; - line.
/quote

In this case IE and Opera are wrong in the return value. Yes
they have done the ECMA 10.1.3 thing in initialising the
activation object but failed to spot the missing paragraph
margin in ECMA-262.pdf section 13 Syntax - tsk tsk - which
Mozilla did not:

cite
NOTE
The Identifier in a FunctionExpression ...
snip
/cite
I disagree here. The content of the above - while - loop is a
FunctionDeclaration not a FunctionExpression so ECMA 10.1.3 and its
requirement that the variable object be instantiated with a function
object from this declaration with the identifier - foo - should apply.
It does apply on Opera and IE and it is Mozilla that is getting this
wrong.

Consider this test function that I posted earlier in the thread:-

function funcTest1(){
var x=[];
for (var i=0;i<2;i++){
function foo(){alert(i);};
x[i] = foo;
}
return (x[0]==x[1]);
}

- the function declaration is identical to the one in the - while -
block but Mozilla is happy to recognises - foo - as an identifier. I
think that rules out the possibility that this code could be being
interpreted as - FunctionExpression Identifier(opt) FormalParameterList
FunctionBody - so the external visibility of the - Identifier(opt) - of
a FunctionExpression is not significant in the interpretation of a
FunctionDeclaration when it appears in an unexecuted - while - block.

Quote:
However, this is not related to whether the browsers might or
might not have joined the foo function in a loop or across
calls, and *IHMO* the programmer who puts a named function
expression in a loop is asking for *what exactly*? What they
get is my answer
Yes, real code should never have a problem with these discrepancies, as
it would be perverse to be declaring functions within code blocks
(function expressions, yes, but not function declarations). However, to
decide whether there is any joining going on, how much (if any) and what
the best response to make to 'normal' browser behaviour takes a few
extreme experiments to try to work out what is happening in the various
implementations.

<snip>
Quote:
My thoughts and observations are that javascript engines are
empirically unlikely to join (closure) function objects across
multiple calls to a container function at this time (I haven't
seen any), but attribute this to the interpretative nature of
javascript (subject to optimisation at any time) rather than
the content of ECMA 262.
Much the same as my conclusion; there is no evidence that current major
browsers implement any function object joining or re-use within other
functions. With obvious implications for the use of inner functions.

Ricahrd.




Reply With Quote
Reply




Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off



Powered by vBulletin Version 3.5.4
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.