Matt said:
After further investigation (see below), I see that my statement is
incorrect, but to answer your question properly: not always. According to
the reference material, the Array constructor was introduced in JavaScript
1.1, so it would evaluate to `false' in that version, too. And in
J(ava)Script 1.0, where `Array' is not defined, it would most certainly
not evaluate to anything (not even `true'), instead it would result in
an error.
However, in J(ava)Script 1.0 the (Syntax)Error would occur before because
`typeof' was not introduced before JavaScript 1.1, JScript 2.0. But either
aspect strikes me as being completely academical nowadays.
I don't remember exactly, but I recall writing the syntax of
if (!Array && (obj instanceof Array))
quite a long time ago, to protect against browsers which didn't have
Array defined. I may very well be in error, though.
In fact, the above does not protect against anything because if there was
no Array constructor, the base object of the reference is `null', and
evaluation would result in a ReferenceError (exception).
This was stated on similar occasions (with different identifiers) several
times in discussions the last days. Testing with any not defined
identifier shows that it is true.
However, to have it not only stated but also explained once (and please
CMIIW), here it is in detail for those who are interested in the language's
inner workings (including curious me who found that investigation quite
interesting). Everybody else please just ignore the following section

___________________________________________________________________________
,-[ECMAScript 3 Final]
|
| 11.4.9 Logical NOT Operator ( ! )
|
| The production UnaryExpression : ! UnaryExpression is evaluated as
| follows:
(First let us [the script engine] make sure that this production can be
used and there is no syntax error. The productions for `Array' are:
Program --> SourceElements --> SourceElement --> Statement
--> ExpressionStatement
--> [lookahead !elementOf {{, function}] Expression --> Expression
--> AssignmentExpression --> ConditionalExpression
--> LogicalORExpression --> LogicalANDExpression
--> BitwiseORExpression --> BitwiseXORExpression --> BitwiseANDExpression
--> EqualityExpression --> RelationalExpression --> ShiftExpression
--> AdditiveExpression --> MultiplicativeExpression
--> UnaryExpression --> PostfixExpression --> LeftHandSideExpression
^^^^^^^^^^^^^^^
--> NewExpression --> MemberExpression --> PrimaryExpression
--> Identifier
^^^^^^^^^^
(--> IdentifierName /but not/ ReservedWord --> IdentifierName
<--> {IdentifierStart, IdentifierName IdentifierPart}
<--> IdentifierPart --> {Unicode*}.)
Obviously `Array' is also a UnaryExpression. Therefore, we [the script
engine] can continue with evaluation.)
| 1. Evaluate UnaryExpression.
| 2. Call GetValue(Result(1)).
| 3. Call ToBoolean(Result(2)).
| 4. If Result(3) is true, return false.
| 5. Return true.
Executing step 1, following the productions of the grammar for `Array'
above, we see that an Identifier token has to be evaluated:
| 11.1.2 Identifier Reference
|
| An Identifier is evaluated using the scoping rules stated in section
| 10.1.4. The result of evaluating an Identifier [is] always a value of
| type Reference.
| 10.1.4 Scope Chain and Identifier Resolution
| [...]
| During execution, the syntactic production PrimaryExpression : Identifier
| is evaluated using the following algorithm:
|
| 1. Get the next object in the scope chain. If there isn't one, go to
| step 5.
| 2. Call the [[HasProperty]] method of Result(1), passing the Identifier
| as the property.
| 3. If Result(2) is true, return a value of type Reference whose base
| object is Result(1) and whose property name is the Identifier.
| 4. Go to step 1.
| 5. Return a value of type Reference whose base object is null and whose
| property name is the Identifier.
|
| The result of evaluating an identifier is always a value of type
| Reference with its member name component equal to
| the identifier string.
It should be obvious that if not even the Global Object (the last object in
any scope chain, see section 10.2) has an `Array' property, i.e. there is
no `Array' constructor, `null' is returned as base object of the `Array'
reference. (For brevity, I will write `Reference(X, Y)' for a value of
type Reference with base object component X, and member name component Y.
So the Reference value here is Reference(null, Array).)
Let us get back to the evaluation of the Logical NOT operator:
| 1. Evaluate UnaryExpression.
Because of the above, Result(1) := Reference(null, Array)
| 2. Call GetValue(Result(1)).
Result(2) := GetValue(Reference(null, Array))
| 8.7.1 GetValue (V)
|
| 1. If Type(V) is not Reference, return V.
It was established that V is of type Reference, so nothing is done here.
| 2. Call GetBase(V).
| GetBase(V). Returns the base object component of the reference V.
It was established that the base object component of V is `null':
Result(2) := null
| 3. If Result(2) is null, throw a ReferenceError exception.
Since the condition applies, the named exception is thrown. q.e.d.
| 5.2 Algorithm Conventions
| [...]
| If an algorithm is defined to "throw an exception", execution of the
| algorithm is terminated and no result is returned.
| The calling algorithms are also terminated, until an algorithm step is
| reached that explicitly deals with the exception, using terminology such
| as "If an exception was thrown...". Once such an algorithm step has been
| encountered the exception is no longer considered to have occurred.
BTW: There is a truly marvelous proof that the `!' operator does not
matter regarding this, which this posting is too short to contain ;-)
___________________________________________________________________________
Back to your code. Your expression was:
Let us assume that the first two subexpressions were evaluated to `true'.
This forces the evaluation of the third subexpression:
(!Array || (o instanceof Array))
If the Array constructor exists (JavaScript/JScript > 1.0), the first
operand evaluates to `false', which forces the evaluation of the second
operand. However, `instanceof' is an operator specified in ECMAScript
Edition 3, not introduced before JavaScript 1.4. A SyntaxError exception
would be thrown in JavaScript 1.1 to 1.3.
If the Array constructor does not exist (JavaScript 1.0, JScript < 3.0),
and we assume that for some reason (which leaves only being JScript 2.0,
see above) the implementation supports the previous `typeof' operator, a
ReferenceError exception is thrown here, as explained in detail above.
Therefore, I would drop this subexpression completely and look for something
more generic regarding objects with numerically iterable properties. I do
not think that this can be done without giving up at least partially
backwards compatibility to JavaScript versions prior to 1.5, and JScript
versions prior to 5.5, though.
I see the following solutions to this dilemma:
A) Forget about backwards compatibility and use o.hasOwnProperty()
(JavaScript 1.5+) to determine if the object with a `length' property
has a property with numerical name that corresponds to the value of
the former. That is, if o.length == 2, test if o[1] exists; if yes,
assume that the object is numerically iterable.
B) Handle only Array objects, test for them with o.constructor == Array
(JavaScript/JScript > 1.0).
C) Handle all objects with a `length' property except String objects,
test with o.constructor == String (JavaScript/JScript > 1.0).
D) Handle all objects with a `length' property of type `number'
(JavaScript 1.1+). In JavaScript, each character of the string
value of String objects would be handled individually (which may be
inefficient); a workaround would have to be found for JScript, where
String objects have no properties with numerical names that
correspond to the characters of the string value.
Therefore, my untested suggestion, which should work in JavaScript 1.1+ /
JScript 2.0+, where collections are not supported before JavaScript 1.5 /
JScript 5.5:
var bHasOwnProperty;
if (typeof o == "object"
&& o.constructor != String
&& typeof o.length == "number"
&& (((bHasOwnProperty = (typeof o.hasOwnProperty == "function"))
&& o.hasOwnProperty(o.length - 1))
|| (!bHasOwnProperty && o.constructor == Array)))
{
for (var j = 0, len = o.length; j < len; j++)
{
// ... o[j] ...
}
}
PointedEars