Newbie Q's: references, scopes, overwrites

D

Dom Bannon

Sorry for the newbie question - I write several languages but my
JavaScript is very limited. I have a problem with some code which
displays an svg file received via Ajax. The top-level script includes
these lines:

1 var svg = xhr.responseXML.documentElement;
2 svg = cloneToDoc(svg); // portable importNode
3 window.svgRoot = svg;
4 document.body.appendChild(svg);
5 delete window.svgRoot; // <- problem

The script then terminates. The incoming Ajax message ('svg') is a
script, that references a couple of additional scripts, and I rely on
one of them to be run when it's loaded (presumably on 'appendChild',
but I'm not sure).

This works, but only if line 5 is commented out. If it's left in, I
get various issues which have the feel of a memory over-write. I don't
really know what to do here, because I don't understand the window
object, or the scoping issues, or much else. The guy who wrote it says
that line 5 is "just cleanup, removing the global svgRoot property
from the window after appending the file".

Dumb question #1: what exactly is 'window'? Is it an object which
represents the currently visible window? Presumably it's still in
scope, and global, and represents the same window when the other (svg)
scripts are running?

#2: what is line 3 doing? Is it auto-declaring a new 'svgRoot' var in
'window'? Can you just do that? Is the author just finding a
convenient location to put a global?

#3: the rest of the code manipulates the svg via window.svgRoot,
rather than getting a reference to the new child in the document body.
Is this Ok? Is line 4 appending a reference to svg, or copying svg, or
somehting else? Presumably line 5 doesn't actually delete the new
child?

#5: is there a memory overwite/leak detect tool for JavaScript?

Thanks if you managed to read this far... :)
 
T

Thomas 'PointedEars' Lahn

Dom said:
Sorry for the newbie question - I write several languages but my
JavaScript is very limited. I have a problem with some code which
displays an svg file received via Ajax. The top-level script includes
these lines:

1 var svg = xhr.responseXML.documentElement;
2 svg = cloneToDoc(svg); // portable importNode
3 window.svgRoot = svg;
4 document.body.appendChild(svg);
5 delete window.svgRoot; // <- problem

The script then terminates.

That is to be expected. But does it terminate with an error? If yes, what
does the error message say?
The incoming Ajax message ('svg') is a script, that references a couple of
additional scripts, and I rely on one of them to be run when it's loaded
(presumably on 'appendChild', but I'm not sure).

This is not going to happen.
This works,

It is generally unreliable. The only way to be rather sure that new script
code will be executed is to append a new `script' element.

This might be an exception if the parent document is declared XHTML 1.1 +
SVG + MathML, or HTML5, and you are actually appending an `svg' element
node, where the script that it contains pertains to and is triggered by SVG
elements only. But your description indicates otherwise.
but only if line 5 is commented out.

Line 5 attempts to delete a property of a host object, which may not be
possible.
If it's left in, I get various issues which have the feel of a memory
over-write.

What issues? How did you get that impression?
I don't really know what to do here, because I don't understand the window
object, or the scoping issues, or much else.

This does not appear to have anything to do with scope.
The guy who wrote it says that line 5 is "just cleanup, removing the
global svgRoot property from the window after appending the file".

That description is correct in general, but they are otherwise clueless.
See below.
Dumb question #1: what exactly is 'window'?

It is a host-defined property of the ECMAScript Global Object which value is
a reference to an object that represents the view of the document that
contains the script code.
Is it an object which represents the currently visible window?

In a sense, yes.
Presumably it's still in scope, and global, and represents the same window
when the other (svg) scripts are running?

This is not a valid question. Please restate your request.
#2: what is line 3 doing?

It is an attempt to create or overwrite the property of the object referred
to by `window', that is named `svgRoot', with the value of the `svg'
variable.
Is it auto-declaring a new 'svgRoot' var in 'window'?
No.

Can you just do that?
No.

Is the author just finding a convenient location to put a global?

Yes, and he might have been misguided by Douglas Crockford's notion that
this was the (proper) way to do that.
#3: the rest of the code manipulates the svg via window.svgRoot,
rather than getting a reference to the new child in the document body.
Is this Ok?

That depends on whether the object referred to by `window' already had a
`svgRoot' property in the first place, whether it has one after the
assignment in line 3, and whether the value of that property is the value of
`svg'.

The property creation may fail because the object referred to by `window' is
a host object (not a native one), and the property value modification may
fail because an existing property of that name was defined to be read-only.
Is line 4 appending a reference to svg, or copying svg, or
somehting else?
Yes.

Presumably line 5 doesn't actually delete the new child?

Please restate your request.
#5: is there a memory overwite/leak detect tool for JavaScript?

Yes.


PointedEars
 
T

Thomas 'PointedEars' Lahn

Thomas said:
It is a host-defined property of the ECMAScript Global Object which value
is a reference to an object that represents the view of the document that
contains the script code.

… or includes it with a `script' element that has a `src' attribute
specified.


PointedEars
 
E

Elegie

On 03/11/2011 19:07, Dom Bannon wrote :

Hello,

1 var svg = xhr.responseXML.documentElement;
2 svg = cloneToDoc(svg); // portable importNode
3 window.svgRoot = svg;
4 document.body.appendChild(svg);
5 delete window.svgRoot; //<- problem

This works, but only if line 5 is commented out. If it's left in, I
get various issues which have the feel of a memory over-write. I don't
really know what to do here, because I don't understand the window
object, or the scoping issues, or much else. The guy who wrote it says
that line 5 is "just cleanup, removing the global svgRoot property
from the window after appending the file".

I am not familiar with SVG, but will try and give you some pointers
regarding your questions below.
Dumb question #1: what exactly is 'window'? Is it an object which
represents the currently visible window? Presumably it's still in
scope, and global, and represents the same window when the other (svg)
scripts are running?

Basically, yes. But let us consider all this from the browser perspective.

A browser will emit and receive HTTP commands. This means that:
- it must provide a way for the user to build and send HTTP commands.
This can be done using a view and capturing user events on this view
(like submitting a form or clicking a link), or by providing actions on
the UI itself (like navigating an address bar or activating a favorite),
- it must process the received commands. Received commands can range
from "display a view which you will build using this HTML", to "here's
an image, use it as a reference when building the current view", or
"here comes a .stuff file, do something about it".

What's important here is that the browser maintains a view with which
the user can interact. It also probably maintains its own controller and
model, leveraging the MVC pattern.

Browser views are built using a component-based model. The top element
of the model is the "window" element, and hierarchically attached to
this window are other elements (like text, images, form controls). HTML,
in a certain way, is the serialized form of the view.

The browser, while rendering the view, will also load engines, which
will be able to manipulate the view in certain ways. For instance, a CSS
engine will be able to apply a certain rendering onto view elements, and
a script engine (using languages like javascript, VBScript,
PerlScript...) will be able to directly manipulate the structure.

Received HTTP commands may contain CSS or script instructions (declared
directly into the HTML), or may reference external resources to be
applied on the view (CSS/script includes). What is nice is that the
browser allows an author to manipulate the view in many ways, providing
a set of more-or-less standardized API.
#2: what is line 3 doing? Is it auto-declaring a new 'svgRoot' var in
'window'? Can you just do that? Is the author just finding a
convenient location to put a global?

This is a tricky question, and the quick answer could be either "yes,
but" or "no, but" :)

What's important to notice, here, is that the "window" object is really
a view element, while the "var" keyword is used to define a javascript
variable in a lexical scope. Both should have nothing in common; but
somehow, most browsers try and join the "window" element with the global
javascript object, making a global declaration using the "var" keyword
roughly (but not completely) equivalent to the property-setting on the
window object.

For a web author, the difference should not matter (though it should be
preferable to use the "var" keyword, as creating "expando properties" -
i.e. properties on host objects in general - may not always be safe).
#3: the rest of the code manipulates the svg via window.svgRoot,
rather than getting a reference to the new child in the document body.
Is this Ok?

Yes, of course, because both are referencing the same object (the SVG
fragment).
Is line 4 appending a reference to svg, or copying svg, or
somehting else?

The SVG variable certains contains a document fragment. The "document"
element refers to a child of the "window" element, and its appendChild
method programmatically adds some nodes to its children. To put it in a
nutshell, it is used to dynamically add elements to the view.
Presumably line 5 doesn't actually delete the new
child?

The "delete" method is used to remove a property from an object. If that
property contains a reference to some object, then this reference is
freed and the object can be garbage-collected (unless other references
to this object remain).

Doing "obj.prop = null;" would suffice, though.
#5: is there a memory overwite/leak detect tool for JavaScript?

I don't know, sorry. But I know that some memory leaks happen in some
browsers, when a circular reference is formed between a view object and
a script object (both objects stay in the memory and are never cleaned
up). A common practice is to free the problematic references, so as to
break the circular reference. Maybe this is the issue that you have been
facing with your script.

Regards,
Elegie.
 
D

Dom Bannon

Dom Bannon wrote:

That is to be expected. But does it terminate with an error? If yes, what
does the error message say?

No, sorry, I meant that that was the end of the script. It's just a
short script that does a one-off Ajax request to load in the other
scripts:

<script type="text/ecmascript"><![CDATA[
setTimeout(function(){
var xhr = new XMLHttpRequest;
xhr.open('get','test2.svg',true);
xhr.onreadystatechange = function(){
if (xhr.readyState != 4) return;
var svg = xhr.responseXML.documentElement;
svg = cloneToDoc(svg); // portable importNode
window.svgRoot = svg;
document.body.appendChild(svg);
delete window.svgRoot; // <- problem
};
xhr.send();
},1000);
function cloneToDoc(node,doc){
...
}
]]> said:
This works,

It is generally unreliable. The only way to be rather sure that new script
code will be executed is to append a new `script' element.

Is the 'document.body.appendChild(svg)' sufficient? This is an xhtml
doc, content type 'application/xhtml+xml'. The script that is loaded
(ie. node 'svg') includes this code, which initialises everything and
produces the image:

if(!window.svgRoot) {
svgRoot = document.documentElement;
}
try {
...init code in one of the other svg scripts
} catch(e) {
...error report
}

Note the test for '!window.svgRoot', despite the fact that the author
previously did a 'delete window.svgRoot' above.
Line 5 attempts to delete a property of a host object, which may not be
possible.


What issues? How did you get that impression?

The immediate error is that both F/F and Opera report that
'group.getBBox()' is not a function call, where 'group' is meant to be
a group node. Note that the svg code itself does actually work when
viewed stand-alone (and this 'getBBox' does work correctly). Tracing
back a bit I find that there is at least one error in
'svgRoot.namespaceURI'; it's set to 'w3.org/1999/xhtml', instead of
'w3.org/2000/svg'.
This does not appear to have anything to do with scope.


That description is correct in general, but they are otherwise clueless.
See below.


This is not a valid question. Please restate your request.

I wanted to find out if it was valid to reference 'window.svgRoot'
from one of the svg scripts but, from what you say elsewhere, it looks
like it is valid.
That depends on whether the object referred to by `window' already had a
`svgRoot' property in the first place, whether it has one after the
assignment in line 3, and whether the value of that property is the value of
`svg'.

I've just traced through the changes to 'window' with Firebug.
'svgRoot' doesn't exist before the assignment to 'window.svgRoot', and
it does exist after the assignment, and it appears to be correct on a
cursory inspection. So, the author does appear to be simply
auto-declaring a var in 'window'.
Please restate your request.

My concern was that 'window.svgRoot' was actually a reference to the
new child created by 'document.body.appendChild(svg)' (line #4), so
deleting the former would actually delete the new child tree. However,
after reading up a bit more, it seems that line #4 actually *moves*
the svg tree from 'window' to the doc body. So line #5 is presumably a
bug anyway, unless it's intended to delete a var which no longer
references anything.

?

Thanks -
Dom
 
T

Thomas 'PointedEars' Lahn

Dom said:
Thomas said:
Dom Bannon wrote:

[…] It's just a short script that does a one-off Ajax request to load in
the other scripts:

<script type="text/ecmascript"><![CDATA[

The proper `src' attribute value here is "text/javascript" or
"application/javascript", with the former one being supported best.
setTimeout(function(){

This should be

window.setTimeout(function() {
var xhr = new XMLHttpRequest;

IMO constructor calls should include the empty argument list, `()'. If
other environments are targeted as well, alternative code might be needed.
xhr.open('get','test2.svg',true);

The HTTP verb is GET, so should be the first argument of xhr.open() here.
xhr.onreadystatechange = function(){
if (xhr.readyState != 4) return;

And interesting optimization that for some reason did not occur to me until
now (I had favored the branching code over that gauntlet). However, what is
missing here is checking the value of the `status' property, i. e. whether
the request was successful (200), before attempting to process the response
message.
var svg = xhr.responseXML.documentElement;
svg = cloneToDoc(svg); // portable importNode
window.svgRoot = svg;
^^^^^^^^^^^^^^^^
AISB, do not do that …
document.body.appendChild(svg);
delete window.svgRoot; // <- problem
^^^^^^^^^^^^^^^^^^^^^
… then you do not have to do that either.
};
xhr.send();

The argument list should be `(null)'.
},1000);
function cloneToDoc(node,doc){
...
}

You omitted relevant code.
]]> said:
This works,

It is generally unreliable. The only way to be rather sure that new
script code will be executed is to append a new `script' element.

Is the 'document.body.appendChild(svg)' sufficient?

That depends on the return value of cloneToDoc(), but generally yes (with
the provision made in my previous article).
This is an xhtml doc, content type 'application/xhtml+xml'.

The including document? The included document? Both?
The script that is loaded (ie. node 'svg') includes this code,

A markup document that has an `svg' element as its root element is not a
script; it is either an SVG (Scalable Vector Graphics) document or an
untyped XML document.
which initialises everything and produces the image:

if(!window.svgRoot) {
svgRoot = document.documentElement;
}
try {
...init code in one of the other svg scripts
} catch(e) {
...error report
}

Note the test for '!window.svgRoot', despite the fact that the author
previously did a 'delete window.svgRoot' above.

This code does not make sense. However, you omitted relevant parts. It
would be best if you created and provided a minimal test case at a publicly
accessible location.
but only if line 5 is commented out. […]
If it's left in, I get various issues which have the feel of a memory
over-write.

What issues? How did you get that impression?

The immediate error is that both F/F and Opera report that
'group.getBBox()' is not a function call, where 'group' is meant to be
a group node.

As we have not seen what `group' refers to yet, that is useless, if
generally pertinent, information at this time.

You have not explained your impression of a "memory over-write".
Note that the svg code itself does actually work when
viewed stand-alone (and this 'getBBox' does work correctly). Tracing
back a bit I find that there is at least one error in
'svgRoot.namespaceURI'; it's set to 'w3.org/1999/xhtml', instead of
'w3.org/2000/svg'.

This code looks more and more bogus.
I wanted to find out if it was valid to reference 'window.svgRoot'
from one of the svg scripts but, from what you say elsewhere, it looks
like it is valid.

It is syntactically valid, but (AISB) rather unwise.
I've just traced through the changes to 'window' with Firebug.

So your target runtime environment is Mozilla Firefox? Which version(s)?
'svgRoot' doesn't exist before the assignment to 'window.svgRoot', and
it does exist after the assignment, and it appears to be correct on a
cursory inspection. So, the author does appear to be simply
auto-declaring a var in 'window'.

Yes, that only appears to be so. I have already explained what really
happens.
My concern was that 'window.svgRoot' was actually a reference to the
new child created by 'document.body.appendChild(svg)' (line #4), so
deleting the former would actually delete the new child tree.

Deleting a property of an object usually does not remove a node from a
document tree. However, as host objects are involved, all bets are off.
However, after reading up a bit more, it seems that line #4 actually
*moves* the svg tree from 'window' to the doc body.

That would depend on what cloneToDoc() does. If it does what its name says
(to clone), then nothing is moved. Certainly nothing is moved "from
'window' to the doc body".
So line #5 is presumably a bug anyway, unless it's intended to delete a
var which no longer references anything.

The whole approach looks like one big bug to me at this point.

<http://catb.org/~esr/faqs/smart-questions.html>


PointedEars
 
A

Antony Scriven

Dom said:
Thomas said:
Dom Bannon wrote:
[...]
#5: is there a memory overwite/leak detect tool for
JavaScript?
Yes.

<http://catb.org/~esr/faqs/smart-questions.html>

Thomas, that's standard English idiom. A plain yes or no
answer is not what Dom asked for. It's not expected to be
interpreted literally, hence the '?'. The strong implication
is that you should provide details if you have them. This
sort of indirection is routinely used to express politeness
in English. --Antony
 

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

Forum statistics

Threads
473,754
Messages
2,569,525
Members
44,997
Latest member
mileyka

Latest Threads

Top