Re: An isArray test (and IE bugs)
- From: "Richard Cornford" <Richard@xxxxxxxxxxxxxxxxxxx>
- Date: Thu, 15 Jan 2009 16:06:32 -0000
kangax wrote:
Richard Cornford wrote:<snip>
... Thus, an object that has a [[Class]] property with a value that is not 'Array' must not be an Array, but an object
with a [[Class]] property that is "Array" might be an Array
_or_ some host object.
This is my main concern as well [2]. Just like with `typeof`
(which is allowed to return implementation-dependent values)
[[Class]]-based `isArray` needs extensive testing across
existent clients.
And by "extensive" you mean that it would be necessary to go through all of (at least types of) the objects provided by a host and verify that none of them did have "Array" as a [[Class]] property. That is quite a difficult task even if you only attempted it on 3 or 4 'common' web browsers.
Recall, for example, that the - item - method of IE's 'collection' objects returns "string" from - typeof - tests and how unexpected that is and how unlikely it was to be noticed without systematic examination of the browser's DOM.
[snip `isArray` tests for later review]
Object.prototype.toString.call(arrayFromAnotherWindow)
- returns "[object Object]' in IE 6 and 7 (with similar
implications for the "The Miller Device" in 'isFunction'
testing caused by the same 'wrapping' of 'remote' objects)
That's not good at all.
With cross-window scripting rapidly retreating into history I doubt that is as much of an issues as it could have been.
I wonder if a check based on non-generic nature of `Array.prototype.toString` returns "proper" result. The
specs are pretty vague about this [3],
They are precisely as vague as step 4 in the - concat - algorithm. The words "is not an Array object" require that the implementation has some means of telling an Array from everything that is not an Array. Failing to deliver that would be an implementation bug, but we have just seen an IE implementation bug in this area.
but in Firefox 3.0.5, for example, this "works" across frames (I doubt it does)<snip>
function isArray(o) {
try {
Array.prototype.toString.call(o);
return true;
} catch(e) { }
return false;
}
Language version questions aside (as at this point (9 years after its release) I don't have a big problem with assuming ES 3 as a minimum), I have never liked the idea of using try-catch for testing. This is in part a consequence of javascript's try-catch being extremely poor and problematic to use. You have no choice but to catch every exception that is thrown in a - try - block, but only the exceptions that you expected to be catching should be handled in the - catch - block. That suggests that each - catch - block should contain the code to identify the exact type and nature of the exception thrown so that all the exceptions that you did not expect to be catching can be re-thrown.
In reality you almost never see this, because it is both too bulky and too difficult. Consider, for example, verifying that the exception thrown was the ECMAScript specified - TypeError - from the Array's - toString - algorithm. You could try - e.name == "TypeError". Then how do you determine that it is the TypeError thrown by the call to - Array.prototype.toString - when the - this - object is not an array. A TypeError has a - message - property that could say something like "Array toString called on non-Array object", but the actual specification says that the message is "an implementation-defined string". That means that the string is likely vary between browsers (and we know full well that they do vary between browsers). So while you could examine the strings in an given set of browsers/browser configurations you are going to hard pressed to be in a position to identify the exception in a browser that you have never seen.
Then there is the language of the message. It is easy for US citizens (and British and other native English speakers) to fall into the trap of disregarding other languages. And it may be the case that javascript engines are pretty mostly created in the US and/or do issue their error messages in English. But there is no certainly of that, indeed it would be entirely reasonable for a JS implementation to issues its error messages in the language of the OS, in which case identifying exceptions by - message - not only requires some sort of testing for the variations that each browser may produce but also all the (200 odd possible) language versions that may be produced by a single browser.
Unsurprisingly, you don't see people verifying that the exceptions they have caught are the exceptions they were expecting to catch with the - message - property (except for very 'fixed environment' code). But ECMAScript does not offer any other error identification mechanism beyond the broad type of the error.
One of the things that I looked at when considering my - isArray - test was the nature of the array that gives it the most problems; the one element array that has a reference to itself as its only elements. It is an unlikely object but still a theoretical possibility. One of the questions I asked myself was what would happen if its - toString - method was called. For that object:-
1. The array's - toString - method calls its - join - method - with ',' as its argument.
2. The array's - join - method calls - ToString(el) - on the array's single element (which is a reference to itself).
3. The internal - ToString - function (via ToPrimitive, etc) calls the object's - toString - method, so GOTO 1.
- and we are headed for a stack overflow of some sort.
JScript and JavaScript(tm) are both smart enough not to fall into this trap (they just return an empty string), but Opera obligingly throws a 'stack overflow' exception. Other browser may also throw exceptions (or just overflow the stack and crash (themselves or the OS)).
This means that you cannot take an exception being thrown by - Array.prototype.toString.call(arg) - as indicating that - arg - is not an Array. Even without the one element array with its only element being a reference to itself you still have to consider that - join - will result in the - toString - methods of any object in the array being called and any exception thrown in any of those calls (which may be a TypeError itself) will propagate up to be caught in your try-catch block (and that is without considering whether those - toString - method calls have side effects (anyone writing a - toString - method that has side effects needs their head examining (VK?))).
If you want to use try-catch for this test you really do have the problem of verifying that any exception thrown is the exception that you expect - Array.prototype.toString - to throw when its - this - object is not an array, and if you do that you will "bloat" this test function by quite a lot.
(It is unfortunate that ES 3.1's 'strict' mode will throw exceptions in circumstances where ES 3 would not, but ES 3.1 is not going to offer any more effective error identification mechanism, or any better try-catch structures. The last thing the ECMAScript committee wants to be doing is creating a 'strict' mode that is unusable/unworkable but without better try-catch that may well be what they end up doing.)
Richard.
.
- Follow-Ups:
- Re: An isArray test (and IE bugs)
- From: kangax
- Re: An isArray test (and IE bugs)
- From: kangax
- Re: An isArray test (and IE bugs)
- References:
- An isArray test (and IE bugs)
- From: Richard Cornford
- Re: An isArray test (and IE bugs)
- From: kangax
- An isArray test (and IE bugs)
- Prev by Date: Re: Closure vs Rewrite
- Next by Date: Re: Use Incoming Variable to Select Pre-Defined Array
- Previous by thread: Re: An isArray test (and IE bugs)
- Next by thread: Re: An isArray test (and IE bugs)
- Index(es):
Relevant Pages
|