VK said:
This sentence contains two mutually exclusive demands,
No it does not. "Most robust" is a relative term and
"backward-compatible" is a condition inside which relative robustness is
to be judged. It may be that given "backward-compatible" the most
relative robustness that can be achieved is not very robust at all, but
there will still be a "most robust".
because the
most robust and backwards-compatible way is (someObj
instanceof Array) // true / false
If true that would be a pity as (someObj instanceof Array) is true for
an entire category of objects that are not arrays and cannot be treated
as arrays. That is, anything created with a constructor that has an
Array as its prototype or an object as a prototype that has an Array on
its prototype chain. Such objects would not have any special interest in
the assignment of 'array index' or "length" properties, and so would not
exhibit the Array-ness that is most likely the characteristic that needs
to be identified by the proposed function.
So the max you can ask is "let lesser robust and lesser
backwards-compatible but without instanceof"
Matt's English is much better than that, and there was nothing wrong
with the question as expressed, beyond its failure to explain what the
proposed - isArray - function was specifically intended to do.
and this pretty much renders the problem from a
practical task into a mind game.
Not rally, an actual specification for the function should reduce the
answer to a specific "it cannot be done" or code that does it one step.
Also from the posted sample it is hard to say what are
you looking for:
It is, but even harder for you as you have never really grasped what a
javascript array actually is.
1) to determine if some object has JavaScript Array
object in its prototype chain (thus inherits the
functionality of JavaScript Array).
The functionality of a javascript array cannot be inherited, only the
methods.
2) to determine if some object implementing JavaScript
Array functionality by its own custom means.
No javascript object can do that, only host objects.
3) to determine if some object has properties with the
same names and types as an Array instance would have
(so for some reason assumed to have the same functionality).
No such assumption is valid as any object inheriting from an array will
have all the properties of an array but will not be an array itself.
Whatever the real purpose is, the posted code accomplishes
(up to some extend) only the position (3).
///////////////
Check for the position (1)
To determine if some object has JavaScript Array object in
its prototype chain (thus inherits the functionality of
JavaScript Array).
JavaScript implements the prototype-based inheritance.
The prototype chain for each new instance is being
activated
Assigned
After.
the moment of creation
- of the instance of a new native ECMAScript object.
The application of the - new - operator to a function reference starts a
process with at least two distinct stages so is better not though of a
'moment of creation', rather a process of creation where a certain
amount of important work (such as the creation of the actual object and
assignment of the prototype chain) precedes the execution of the
constructor function's body code.
and it is only side-connected (or more often not
connected at all) to the involved function-constructors.
It is actually very important to a constructor that the new object's
prototype chain has been established before the constructor body code is
executed. It allows a constructor to call the methods of the new object
as they have been defaulted on the object's prototype chain.
This way [constructor] property check is
irrelevant to the prototype matter. I'm really disappointed
to see *such* [constructor] usage in *your* code.
It is only not-valid to reject all objects that do not have an Array
constructor as their - constructor - property as being Arrays in a world
where programmers are insane enough to re-assign values to -
Array.prototype.constructor - or assign - constructor - properties to
Array instances. Programmers who know that they are doing will just not
do that, but I can see why that test may concern you.
Once again I suggest to everyone willing to understand
the JavaScript object model to read and to study the
article at
<
http://blogs.msdn.com/ericlippert/archive/2003/11/06/53352.aspx>
Advice on the understanding of javascript is not credible when it
originates from someone who so self evidently does not understand
javascript himself:-
<URL:
http://groups.google.com/group/comp.lang.javascript/browse_frm/thread/64
ae20ba5c760c6/2820fbcd4b4ab7f8 >
The created prototype chain is immutable and it is the
only robust and natural way to check the object kind
in JavaScript.
Knowing that another object is on the prototype chain of another object
is nowhere near sufficient in answering the question of identifying
objects that are Arrays and excluding objects that are not. You may be
able to dismiss all objects that do not have - Array.protoype - on their
prototype chain but you cannot then assume that the non-excluded set are
Arrays.
The obvious and natural way to check the prototype chain
against some known object is instanceof operator. It was
specially made for it, so there is absolutely no reason
to substitute it with any home-made tools.
Beyond its inability to answer the pertinent question.
instanceof operator is supported at least back to
JavaScript 1.2 (Netscape 4.x)
This would be "supported at least back to JavaScript 1.2" in the sense
of 'introduced in JavaScript 1.4 and first available in Netscape 6+and
Mozilla 0.9'.
and JScript 5.0 (IE 5.0). This way I don't understand
what "backwards-compatible" issues could it possibly
raise.
Not knowing when and where language features were introduced can make
seeing back-compatibility issues harder to spot.
At the same time I know that there is some "opposition"
to instanceof usage in JavaScript.
Such as observing that it is not very back-compatible and not necessary
at all and not very useful.
It is caused by the fact that some class-based
languages do have instanceof operator as well, so
using instanceof in JavaScript considered by some
as "betraying the prototype environment"
Nonsense.

. This is a silly allusion because instanceof
in JavaScript and say instanceof in Java share
nothing but their names.
And - instanceof - is very useful in Java, particularly as it allows
code to verify the safety of something like casting to an interface. In
javascript there is no casting, being an instance of a 'class' that
implements an interface does not guarantee the ability to use that
interface on the object and runtime assignments to the - prototype -
properties of constructors can throw the - - instanceof - operator off,
i.e:-
function AnObject(){
;
}
var obj = new AnObject();
AnObject.prototype = {};
alert((obj instanceof AnObject)); // alerts false;
- so an object that is constricted with the right hand operand of -
instanceof - might still return false form that operation.
instanceof in JavaScript is in fact a "syntactic sugar" for
obj.prototype.isPrototypeOf(instance)
And the same issue with the runtime assignment to the constructor's
prototype applies here.
Note:
It is not in a connection with the topic, but as long as we
started on instanceof operator:
With its real mechanics revealed as
obj.prototype.isPrototypeOf(instance)
That is not its real mechanics, that is an alternative that employs an
almost identical algorithm.
it becomes clear why each
instance is also instanceof Object
ArrayObject instanceof Array; // true
ArrayObject instanceof Object; // true as well
The latter is true because any object has Object prototype
in its chain, so all objects could share the same basic
Object functionality. People who are not aware about the
inheritance principles are getting confused sometimes by
this
It was certainly a subject that was confusing you just two months ago
when you wrote:-
<URL:
http://groups.google.co.uk/group/comp.lang.javascript/msg/c44f32b4566384
53 >
- but since I explained it to you there you seem to have started to
grasp prototype based inheritance. Though you still have some way to go.
I would anticipate another two years before you rid yourself of your
remaining misconceptions on the subject and get a real handle on the
issue (base on your not having got it after your first ten years of
using javascript).
pseudo-"double nature" of JavaScript objects.
It would speed your gaining an understanding of javascript is you would
refrain form making up your own jargon. It just leaves you talking a
different language from everyone else, and that will make understanding
what you are told difficult.
Some of them even consider it as some "failure" of
instanceof operator (to a great fun of anyone reading
such statements).
You realise that most of the time what you perceive as the attitudes of
others are in fact just the consequences of your not understanding what
you are being told by the better informed. I have never seen any
evidence of anyone (at all) expressing this belief. And that is often a
common thread in your posts, where you start going on about how people
are concerned with this or that when in reality nobody is even
interested in whatever it is you are whittering on about. A case in
point is in:-
<URL:
http://groups.google.co.uk/group/comp.lang.javascript/msg/3bc6c90a43d991
0f >
- where you start going on about:-
| This is the point where different amateurish manuals starting
| to advise do not use unary minus: which is a 100% pure b.s. of
| course because at no circumstances the program *logic* can be
| affected by external textual parser.
- in the context of using the pre-decrement operator (not unary minus)
with script code inside <!-- ... --> comments in an XML file. This
impression of yours is a complete fantasy of your own creation because
in XML comments may be stripped from the source prior to parsing, and so
nobody is ever going to wrap them round content that is supposed to be
data, and doing so would effectively remove the data from the XML. There
is not, and never has been (outside of your own head), any issue with
using pre/post decrement operators in that context because that context
is itself a fiction.
To sum the things up, instanceof operator is the only
one really reliable object check tool specially made
for it.
Or it is a non-back-compatible operator that may be able to exclude
objects that could not be Arrays but cannot in itself verify that any
given object is an Array.
It is inadequate for the task in hand. And you yourself pointed out
that - isPrototypeOf - can produce equivalent results (so "must be used"
is false anyway), but while you cannot feature test for support for an
operator in the language you can feature test for a method of objects.
- unless for pure sport one wants to support very ancient
and not used anymore platforms.
///////////////
Check for the position (2)
To determine if some object implementing JavaScript Array
functionality by its own custom means.
This is the most simple one: there cannot be such JavaScript
object, so there is no need for such check.
In the same way as your preceding option could not exist in javascript
(Array functionality cannot be inherited).
Note:
There can be DOM object with such functionality if it has
the needed behavior/binding attached.
There can also be host object with the Array functionality (and language
extensions).
While the answer itself is very simple, the question
of why such object is not possible may need some more
explanations.
It is fairly simple to explain, and follows from the inability of client
side code to replace the object's internal [[Put]] method. While it
would be theoretically possible to use the - watch - or -
__defineSetter__ - extensions in JavaScript(tm) to reproduce the side
effects of assignments to all possible 'array index' properties and
the - length - property of an object (and so fully reproduce the
behaviour required of the internal [[Put]] method on a custom object the
very large number of possible 'array index' properties that would need
to be watched would make the creation of such an object very time
consuming and probably a practical impossibility due to restrictions in
available memory.
There are "fields" and "compound properties".
Ah, you are going to do one of your infamous 'off the top of your head'
fantasies.
A field is what it names implies - a primitive data field
one can set, get and (if allowed) delete.
var obj = new Object;
obj.foo = 'bar';
Property "foo" in the sample above is a field.
Besides that there are compound property with getter and setter.
The core difference between compound property and field is that
the value itself stored internally as a separate private
variable. The only way to change it is by using getter and
setter methods.
var arr = new Array(1,2,3);
window.alert(arr.length);
arr.length = 1;
In the sample above length is a compound property. By
reading/assigning its value we are really calling getter
and setter methods, the value itself is not directly
accessible. Because any call to a compound property goes
only through a method, such properties have one very
important advantage over fields:
Compound properties allow to establish relations of any
complexity between property change / read actions and other
methods and properties. Say an assignment to the array
length (thus usage of length property setter) trigs the
array elements removal if the new length is smaller than
the old one.
A side effect following from an assignment to a named property.
Now how is it all connected to a possible Array mimicking?
Oh, do tell.
The deal is that the [length] is the *only one* compound
property existing in the basic JavaScript, everything else
are fields.
The only one? So no assignment to any other property will ever have a
side effect on a property that is not the subject of the assignment?
That would be an inevitable consequence of the mechanism you are
describing where the only " compound property" in the entire system was
the - length - property of array objects.
Note:
Not all of authors do always understand the unique status of
[length]
Here you go again, attributing beliefs to others where no evidence
exists that anyone has these beliefs.
as the only one compound property in the entire language.
You said it again " one compound property in the entire language", so
defiantly no other assignments will have side effects, no matter what.
They still feel that there is "something" about [length]
making it different from say obj.foo. But because of lack of
knowledge they limit the explanations by "special property",
"self-updating property" and the like.
Who does this? The informed attribute all special array behaviour
relating to assignment to the special internal [[Put]] method of arrays.
Moreover basic JavaScript does *not* allow to create new
compound properties. The [length] is only one and there
cannot be any others
That is three times, we can be very certain that the varsity of this
'explanation' is tied 100% to the 'fact' that assignment operations to
any other property of any object in the system can have a side effect on
another property. There is no question that this a matter of
interpretation, a mistranslation, or a misquote; if any other assignment
can be demonstrated as having a side-effect in standard javascript this
entire 'explanation' is just false (that it is the 'off the top of your
head' fantasy that I suspected from the outset).
(again: in the basic scripting environment).
Fine.
Now it becomes clear why there cannot be any "self-made"
arrays: because there is no way to implement the [length]
property.
Well, with the - watch - extension it would be entirely possible to
implement an array-like length property, Granted that is not in the
basic scripting language, but it does raise a significant objection to
your 'explanation' above, because while it would be trivial to implement
an array like - length - property on any object with the - watch -
method (explicitly script the side effects of assignments to the length
property) it would still not be practical to implement an array like
object with the - watch - method.
The problem is that that is not just one property that may have side
effect when an assignment is made, each array has (2 to the power of 32)
such properties. An assignment to any 'array index' property, where the
index is grater than or equal to, the length property will have a side
effect on the - length - property, it will increase it. It is the
impracticality of using the - watch - method to trigger any possible
side-effect for each such assignment that precludes an emulation of an
array with other object.
However, your explanation of a single "compound property" is not only
failing to explain these other side-effect producing assignments, but
also suffers from the same practical limitation, as if a property
specific setter is the means of producing the side effect your
explanation needs (2 to the power of 32) additional setters, and that is
going to be impractical even in native code.
The explanation if much simpler; it is that all javascript objects have
an internal [[Put]] method that is used for assignments, that this
method is passed the property name and the value that is to be assigned,
and that Arrays have a single special [[Put]] method that examines the
property name to see if it is an array index property name or "length"
and if it is performs the appropriate side-effect on the Array object to
which the [[Put]] method belongs. That is how the language specification
say javascript should behave, and is sufficient to fully explain the
behaviour observed.
You either use *that* Array, or you have to use custom
length() method instead of length property.
This makes possible to implement a tedious "arrayness"
check like:
function isArray(obj) {
var ret = false;
if (typeof obj.length == 'number') {
var len = obj.length;
obj[len] = 'probe';
Isn't this 'test' predicated on the fact that assigning to an 'array
index' property of an Array does produce a side-effect on its - length -
property? Where you not just 'explaining' how that was not possible in
javascript because the 'array index' properties are not your "compound
properties"? While I don't expect someone as irrational as you to
produce internally consistent posts you could at least make an effort
not to obviously contradict yourself.
ret = (len < obj.length);
delete obj[len];
}
return ret;
}
Any test that is intended to differentiate between objects that are
arrays and objects that are not arrays should not be designed to
permanently alter the object that is tested. Here any array that passes
the test will find itself with a - length - property that is one longer
than it was to start with, which is pretty important given what an array
is. (All javascript objects, including arrays, use the same internal
[[Delete]] method, and it has no side-effects).
There is only one object in JavaScript physically ever
capable to pass the above check
(where setting one property automatically
reflected in another property).
Weren't you saying that only assignments to the - length - property has
such side effects? While this property has a name that is the
type-converted to string equivalent of the value of the - length -
property.
This object will be either an array instance
Or a host object with array-like behaviour.
or an object instance having Array.prototype somewhere
in its prototype chain.
Nonsense. Having an array in an object's prototype chain does not confer
this "side-effect on assignment to 'array index' properties" on such an
object, because the internal [[Put]] method is a property of each
individual object and cannot be inherited. Any object that inherits from
an Array can still only have the standard object [[Put]] method, which
does not do side effects when used.
At the same time I want to stress once again that this
*tedious* and *ridiculous* isArray check is not needed.
Not that test, but a better implementation of a similar test (that does
not leave the objects tested altered by the testing) would be the only
type of testing that would be capable of identifying objects which
behaved as if they had the special Array [[Put]] method.
function isArray(obj){
var len, res = false;
if(
(typeof (len = obj.length) == 'number')&&
((len >>> 0) == len)&&
(typeof obj[len] == 'undefined')
){
obj[len] = null;
res = (obj.length > (obj.length = len));
delete obj[len];
}
return res;
}
- would be better, but still suffers from potentially moving an
inherited - length - property form the prototype of a non-array object
onto the object itself.
JavaScript has specially
made instanceof operator for that.
Having made the mistake of thinking that array-ness could be inherited
through the protyo00e chain you might think that.
It also means that there is no need for any custom
functions like isArray(), isDate(), isRegExp() at
all - if (obj instanceof Something) does all of it.
So maybe it is a pity that it does none of it.
To make a long story shorter: the only practical check
in array could be for the presence of particular methods.
<snip>
This would be "practical" in the sense of not ending up knowing whether
the object in question was an array or not (very similar to using
instanceof in that regard).
Richard.