Consider this code snippet:
var xml:XML = <outer><inner>null</inner></outer>;
var xmlList:XMLList = xml.inner;
trace(xmlList == null);
What does the trace statement output, true or false, and why?
Well, first, let's stipulate that the "innner" element in the XML object
contains the String "null". It's not an empty element, for those who might
think that "null" is interpreted as a literal null.
Second, the expression "xml.inner" returns a non-null XMLList object, which is
assigned to the variable xmlList. If you were to dereference the xmlList
variable, say by calling its "length()" method, it would assuredly not throw a
#1009 null reference error, but rather it would return 1, which is the number
of child elements of "outer" that have the name "inner". So the "inner" element
is the only member of xmlList.
So now you have reference to a non-null XMLList of length 1. It seems obvious
that comparing that reference to null with the equality operator (==) should
evaluate to false, right?
Well, it doesn't. The expression:
xmlList == null
evaluates to true, and that is what the trace statement outputs in the original
example.
After spending about 4 hours looking in all the wrong places for a bug that was
actually caused by this odd little quirk, once I found out what was really
causing the problem, I thought for sure that it had to be a bug in the
ActionScript or E4X implementation. After all, it can't be right that a
non-null object reference is considered equivalent to null, right?
Wrong again. If you read the ASDoc for the equality operator, that's what it's
supposed to do. In the Flex 3.2 Language Reference, the section on the equality
operator says:
"If the data types of the operands do not match, the result is false except in
the following circumstances:
...
One operand is of type XMLList, and either of the following conditions is true:
* The length property of the XMLList object is 0, and the other object is
undefined.
* The length property of the XMLList object is 1, and one element of the
XMLList object matches the other operand."
In our case, the length "property" (poor choice of words, since .length would
return another XMLList) is 1, so the result will be based on a comparison
between the one element of the XMLList, which is an XML object, and the other
operand, which is null.
So what does the equality operator doc say about XML objects to non-XML
operands? It says the result is false unless "one operand is of type XML with
simple content (hasSimpleContent() == true), and after both operands are
converted to strings with the toString() method, the resulting strings match."
So here, we do in fact have an XML object, the "inner" node, with simple
content, i.e. the String "null". And when you convert that XML object to a
string with the toString() method, you will simply have the string "null". And
when you convert the OTHER OPERAND, which is null, to a string, you will also
have "null", and therefore the equality operation will return true.
So there you have it. It's working the way it was designed to work. A null is
converted to the string "null" for the purpose of an equality comparison.
Personally, I think it was a poorly thought-out design decision, but hey, what
do I know? Maybe they had their reasons. I think the language designers should
have made a separate rule or two to cover cases when one operand is null or
undefined and the other is not.
And as a side note, if the strict equality operator (===) is used, then the
expression:
xmlList === null
evaluates to false, as you would expect.