> > on EACH LOOP we run the query AND the property lookup, instead of just
> > getting a value of the amount of nodes found.
>
> That's incorrect. The value of x is the return value from
> document.getElementsByClass("foo"). Sure, functions can return
> functions, but in this case, the return value is a node list. There
> may be other reasons not to do this in this way, but the argument that
> a function is going to be executed each time x.length is referenced is
> not one of them.
Question 1/ Is myList.length [for NodeList and for Array] executing a
whole lot of code before giving me the value ?
Yes, functions can return functions, but this is not the case here,
but still a whole lot of code can still be executed before you have
your value.
The function getElementsByName (or Class, or TagName, or Selector, or
whatever existing or forthcoming DOM function doing the same thing)
returns a NodeList Object.
Looking at NodeList in the W3C DOM definition (
http://www.w3.org/TR/
REC-DOM-Level-1/level-one-core.html#ID-536297177)
<quote>The NodeList interface provides the abstraction of an ordered
collection of nodes, without defining or constraining how this
collection is implemented.</quote>
So no one [except studying all possible browsers' code in depth] can
in fact tell what's exactly happening under the hood, and shoud not
even bother.
Array.length has the same thing going on, you now - on a scripting
level - that the value changes when the size of the array changes, but
it depends on the implementation internals, and you should never rely
on it being coded one way or another.
Remember objects are pretty complex things, even if in JS many people
tend to mainly use them as single dictionaries, they can do a lot
more.
The thing here is the difference between member variables and member
properties:
- variables store values, and when the variable is accessed, the value
is returned, they are really just allocated memory slots, no code
except assigning or accessing the value in memory
i.e. o.a will only return the value stored in the memory.
- properties are a language conveniences for wrapping usually two
methods [a getter and a setter] (one if read-only or write-only), this
time when you access or write into the property, some code is
executed, instead of just returning the value in memory
i.e. o.b will compute, and could even have to make a synchronous JSON
call, a user prompt or any other heavy computation before giving a
value back
Using properties (and if they are nor widely used in JS, they are in
some high level Object Oriented Languages), one could create a length
implementation that will compute length on each access, or only the
first time it is accessed, or just return the value stored that would
be modified by list modifications.
Enclosed are two test cases, one with getElementsByName, one with a
property definition.
So when not sure if what you access is a property and a value, and if
you rely on it not changing, you could potentially store it in a local
variable.
Remember though that accessing myList[n] is equivalent to myList.item
(n) (this is for DOM), and if the browser's implementation is such
that length always performs the query, it will probably do so for
getting the item too, so you will come from calculating 2*n the list
(one for length, one for item per iteration) to only n+1 computations,
which algorithmically is still O(n), so not much difference.
Working back on the main algorithm to avoid looping through DOM
queries is a much better improvement, but is harder to achieve.
++
Julien
-----Test cases
Here is a test case (tested in IE [5.5, 6, 7, 8, 8 Compatibility],
Safari 4 Windows, Chrome 3, Firefox 3, Opera 9):
This test case shows that on all major browsers, the value of the
length property of a NodeList updates when nodes are created.
------
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "
http://www.w3.org/
TR/html4/strict.dtd">
<TITLE>Nodelist autorefresh feature</TITLE>
<SCRIPT LANGUAGE="javascript">
function addEventHandler(element, eventName, handler) {
if(element.addEventListener) {
element.addEventListener(eventName, handler, false);
} else if(element.attachEvent) {
element.attachEvent('on' + eventName, handler);
}
// Browsers managing neither addEventListener nor attachEvent are
outside the scope of this test
}
var globalElements = document.getElementsByName("myName");
var globalCount = globalElements.length;
function countElements(eventArgs) {
if(! eventArgs) {
eventArgs = event;
}
if(eventArgs) {
if(eventArgs.preventDefault) {
eventArgs.preventDefault();
} else {
eventArgs.returnValue = false;
}
}
var localElements = document.getElementsByName("myName");
var text = "GlobalElements: " + globalElements.length + " /
globalCount: " + globalCount + " / LocalElements: " +
localElements.length;
var newElement = document.createElement("A");
newElement.setAttribute("NAME", "myName");
newElement.setAttribute("HREF", "#");
if(newElement.getAttribute("NAME") != "myName") {
// IE < 8 buggy setAttribute support
newElement = document.createElement('<A NAME="myName" HREF="#"/>');
}
addEventHandler(newElement, 'click', countElements);
newElement.appendChild(document.createTextNode(text));
document.body.appendChild(newElement);
document.body.appendChild(document.createElement("BR"));
}
addEventHandler(window, 'load', countElements);
</SCRIPT>
------
Here is a test case (tested in Safari 4 Windows, Chrome 3, Firefox 3,
Opera 9 [Only IE doew not support JScript properties]):
This test case shows that defining a property may not do simple
variable assignment and can do computation on each access.
-->
https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Creating_New_Objects/Defining_Getters_and_Setters
------
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "
http://www.w3.org/
TR/html4/strict.dtd">
<TITLE>JavaScript Property Support</TITLE>
<SCRIPT LANGUAGE="javascript">
var o = {
a: 5,
get b() {
return this.a > 5 ? "greater" : "lower";
},
set b(x) {
if(! isNaN(Number(x))) {
this.a = x * 2;
} else {
alert("please type a number, current type " + typeof(x));
}
}
};
alert("o.a = " + o.a + "\r\no.b = " + o.b);
o.a = 10;
alert("o.a = " + o.a + "\r\no.b = " + o.b);
o.b = 2;
alert("o.a = " + o.a + "\r\no.b = " + o.b);
o.b = 3;
alert("o.a = " + o.a + "\r\no.b = " + o.b);
o.b = "toto";
alert("o.a = " + o.a + "\r\no.b = " + o.b);
</SCRIPT>