[Lou, maybe there is a place in the documentation where this could be
discussed. I think it may not be well known.]
We recently had a bug report that the debugger started issuing a
warning in code where it had not before:
WARNING: reference to undefined property 'length'
The initial fix proposed was to simply change:
foo.length
to:
foo.['length']
In Javascript, these two expressions have identical meaning. The
second form is normally used if you are trying to access a property
whose name is stored in a variable. For instance, to enumerate the
properties of an object you can say:
for (var key in myobject) ... myobject[key] ...
But in LZX script, there is one other subtle difference. When you use
`.` to access a property, if the property does not exist, you will get
a warning. If you use `[` to access that non-existent property, you
don't. In either case, the value of the expression will be `undefined`.
We did this because we wanted a way to help users discover type-ohs.
We wanted to be able to warn users that they were trying to access a
non-existent property -- perhaps because the spelled the property
incorrectly, or perhaps because the object they were trying to get the
property from wasn't the right object. But we also recognized that
there were certain cases where the user needed to test if a property
existed or not, without eliciting a warning. There needed to be a way
for the user to say "I know what I am doing".[1] But, if you do this,
you really do need to know what you are doing!
As an open source project, OpenLaszlo has lots of people working on
it, and sometimes, despite the best intentions, one person working on
the code may not fully understand what another person's code is trying
to do. In this case, a well-intentioned change[2] had been mis-
applied. A developer who was fixing a dangerous array iteration idiom
mistakenly applied it to a value that was an object (being used as a
map or hashtable), not an array. Hence the warning about no
'length'. (They can be forgiven: the code was a doubly-nested loop
with no comments and variables with names that gave no clue what they
were being used for.)
If we had taken the simple fix -- silencing the warning by using the
`[` operator instead of `.` -- the erroneous iteration would have gone
undetected. Eventually, someone would have run into a bug that was a
consequence of masking the iteration bug. But it might have been much
harder to find then.
There are several lessons to be learned:
1) When you get a warning appearing in code that previously worked,
you _really_ want to understand why that warning started showing up
all of a sudden. The simplest way of silencing the warning may not be
the correct fix.
2) When working on a piece of code you are unfamiliar with, you
really need to take the time to understand the code before you make
changes.
3) When writing a complex piece of code, use mnemonic names for your
variables (local names are 'free', you don't make your code smaller or
faster by using short names), and if it's really tricky code, throw in
a comment!
---
[1] There are a number of other ways to look at object properties, and
we used to recommend asking:
'length' in foo
but in Javascript 2 (and AS3), the `in` operator only applies to
dynamic properties of an object. The `in` operator does not work on
instance properties, e.g.:
class bar {
var zot;
}
var boo = new bar();
'zot' in boo => false
boo.zap = 42
'zap' in boo => true
So if you need to make a test for the existence of a property that
might be dynamic or might be an instance property, the only way to
cover your bases is to use '['. In the AS3 compiler, you will get an
error at compile time if you use `.` to access a property that the
compiler can prove does not exist (because of type inferencing). So
the AS3 compiler has adopted the same convention as LZX: If you use
'[' to access a property, the compiler assumes you are looking for a
dynamic property that may or may not exist at runtime, so it will not
issue a warning.
[2] Lots of people take a shortcut in iterating over an array by saying:
for (var i in myarray) ... myarray[i] ...
But this is dangerous. Because Array's in Javascript are also
Object's, you can add arbitrary properties to an array. If your loop
is only supposed to be looping over the numeric indices of the array,
the above loop will break. The correct way to iterate over the
elements of an array is:
for (var i = 0, len = myarray.length; i < len; i++) ...
myarray[i] ...