Robust isArray() ?

Discussion in 'Javascript' started by Matt Kruse, Dec 14, 2006.

  1. Matt Kruse

    Matt Kruse Guest

    I'm seeking the most robust and backwards-compatible (ie, no instanceof)
    isArray function.

    Here's what I have:

    function defined(o) {
    return typeof(o)!="undefined";
    }
    function isArray(o) {
    // If these conditions aren't met, it certainly isn't an Array
    if (o==null || typeof(o)!="object" || typeof(o.length)!="number") {
    return false;
    }
    // Check to see if the object is an instance of the window's Array object
    if (defined(Array) && defined(o.constructor) && o.constructor==Array) {
    return true;
    }
    // It might be an array defined from another window object - check to see
    if it has an Array's methods
    if (typeof(o.join)=="function" && typeof(o.sort)=="function" &&
    typeof(o.reverse)=="function") {
    return true;
    }
    // As a last resort, let's see if index [0] is defined
    return (o.length==0 || defined(o[0]));
    };

    Suggestions?

    --
    Matt Kruse
    http://www.JavascriptToolbox.com
    http://www.AjaxToolbox.com
    Matt Kruse, Dec 14, 2006
    #1
    1. Advertising

  2. Matt Kruse wrote:

    [snip]
    > // It might be an array defined from another window object - check to see
    > if it has an Array's methods
    > if (typeof(o.join)=="function" && typeof(o.sort)=="function" &&
    > typeof(o.reverse)=="function") {
    > return true;
    > }

    [/snip]

    Could you could do a toString instead on the constructor, and check
    that?

    > // As a last resort, let's see if index [0] is defined
    > return (o.length==0 || defined(o[0]));
    > };


    Would that catch NodeLists and HTMLCollections?

    I.e. are you looking for objects that are genuine JavaScript "Arrays"
    or objects which have a certain minimum interface, like "length" and
    "[]" index access.

    I note also that "arguments" in IE has "Object" as its constructor, but
    "Array" in Opera or Firefox I think.

    Regards
    Julian Turner, Dec 14, 2006
    #2
    1. Advertising

  3. Matt Kruse

    Matt Kruse Guest

    Julian Turner wrote:
    > Could you could do a toString instead on the constructor, and check
    > that?


    I don't think so, because that can vary between browsers and doesn't return
    just a single string like "Array".

    >> // As a last resort, let's see if index [0] is defined
    >> return (o.length==0 || defined(o[0]));
    >> };

    > Would that catch NodeLists and HTMLCollections?


    Yup.

    > I.e. are you looking for objects that are genuine JavaScript "Arrays"
    > or objects which have a certain minimum interface, like "length" and
    > "[]" index access.


    The latter. Something that must (or can) be iterated over, rather than
    interacted with directly.

    > I note also that "arguments" in IE has "Object" as its constructor,
    > but "Array" in Opera or Firefox I think.


    The provided functions works in both cases.

    I guess one thing I'm also looking for is something that is most certainly
    NOT an array, but returns true with the provided function. Then, depending
    on if the test case is practical or not, I can re-think it. I don't think a
    perfect solution exists.

    --
    Matt Kruse
    http://www.JavascriptToolbox.com
    http://www.AjaxToolbox.com
    Matt Kruse, Dec 14, 2006
    #3
  4. In comp.lang.javascript message <>, Thu, 14
    Dec 2006 11:03:11, Matt Kruse <> wrote:
    > ...
    > // As a last resort, let's see if index [0] is defined
    > return (o.length==0 || defined(o[0]));


    If something with contents matching those of o = [,1] can get that
    far, the code will return false in IE6.

    But, on the principle of Feature Detection, rather than trying to see
    what it "*is*", should one not be testing whether it will *do* what one
    wants it to do? If there is, for example, no intention of using its
    (possible) .Sort method, then there should be no need to test for .sort
    - unless one believes in the Shibboleth Method.
    --
    (c) John Stockton, Surrey, UK. ?@merlyn.demon.co.uk DOS 3.3, 6.20; WinXP.
    Web <URL:http://www.merlyn.demon.co.uk/> - FAQqish topics, acronyms & links.
    PAS EXE TXT ZIP via <URL:http://www.merlyn.demon.co.uk/programs/00index.htm>
    My DOS <URL:http://www.merlyn.demon.co.uk/batfiles.htm> - also batprogs.htm.
    Dr J R Stockton, Dec 14, 2006
    #4
  5. Matt Kruse wrote:
    > I'm seeking the most robust and backwards-compatible (ie, no instanceof)
    > isArray function.


    How backwards do you have to go? JavaScript 1.0 didn't have arrays at all.

    function isArray(v) {
    return v && typeof v === 'object' && typeof v.length === 'number' &&
    !(v.propertyIsEnumerable('length'));
    }

    It thinks that the arguments object is an array (which it ought to be (but isn't)).

    http://javascript.crockford.com/
    Douglas Crockford, Dec 15, 2006
    #5
  6. Matt Kruse

    Matt Kruse Guest

    Dr J R Stockton wrote:
    >> // As a last resort, let's see if index [0] is defined
    >> return (o.length==0 || defined(o[0]));

    > If something with contents matching those of o = [,1] can get that
    > far...


    It wouldn't.

    > But, on the principle of Feature Detection, rather than trying to see
    > what it "*is*", should one not be testing whether it will *do* what
    > one wants it to do? If there is, for example, no intention of using
    > its (possible) .Sort method, then there should be no need to test for
    > .sort - unless one believes in the Shibboleth Method.


    Please do tell how you would "feature detect" whether an object can be
    enumerated over using numeric indexes?

    I don't see any fool-proof way to do it, thereby requiring a more complex
    isArray function and inferring features rather than detecting them.

    --
    Matt Kruse
    http://www.JavascriptToolbox.com
    http://www.AjaxToolbox.com
    Matt Kruse, Dec 15, 2006
    #6
  7. Matt Kruse

    Matt Kruse Guest

    Douglas Crockford wrote:
    > How backwards do you have to go? JavaScript 1.0 didn't have arrays at
    > all.


    Then it would be hard to detect them ;)

    > function isArray(v) {
    > return v && typeof v === 'object' && typeof v.length === 'number'
    > && !(v.propertyIsEnumerable('length'));
    > }


    How compatible is propertyIsEnumerable?

    > It thinks that the arguments object is an array (which it ought to be
    > (but isn't)).


    It behaves like one, so IMO it should return true.

    --
    Matt Kruse
    http://www.JavascriptToolbox.com
    http://www.AjaxToolbox.com
    Matt Kruse, Dec 15, 2006
    #7
  8. Matt Kruse

    Randy Webb Guest

    Matt Kruse said the following on 12/14/2006 12:03 PM:
    > I'm seeking the most robust and backwards-compatible (ie, no instanceof)
    > isArray function.
    >
    > Here's what I have:
    >
    > function defined(o) {
    > return typeof(o)!="undefined";
    > }
    > function isArray(o) {
    > // If these conditions aren't met, it certainly isn't an Array
    > if (o==null || typeof(o)!="object" || typeof(o.length)!="number") {
    > return false;
    > }
    > // Check to see if the object is an instance of the window's Array object
    > if (defined(Array) && defined(o.constructor) && o.constructor==Array) {
    > return true;
    > }
    > // It might be an array defined from another window object - check to see
    > if it has an Array's methods
    > if (typeof(o.join)=="function" && typeof(o.sort)=="function" &&
    > typeof(o.reverse)=="function") {
    > return true;
    > }
    > // As a last resort, let's see if index [0] is defined
    > return (o.length==0 || defined(o[0]));
    > };
    >
    > Suggestions?


    var myArray = new Array();
    myArray[0] = "Its an array";
    myArray[1] = "It really is an array!";
    alert(isArray('myArray'))

    IE7: false
    Opera 9: false
    Firefox 2.0: false

    --
    Randy
    Chance Favors The Prepared Mind
    comp.lang.javascript FAQ - http://jibbering.com/faq
    Javascript Best Practices - http://www.JavascriptToolbox.com/bestpractices/
    Randy Webb, Dec 15, 2006
    #8
  9. Matt Kruse

    Randy Webb Guest

    Randy Webb said the following on 12/14/2006 11:05 PM:
    > Matt Kruse said the following on 12/14/2006 12:03 PM:
    >> I'm seeking the most robust and backwards-compatible (ie, no
    >> instanceof) isArray function.
    >>
    >> Here's what I have:
    >>
    >> function defined(o) {
    >> return typeof(o)!="undefined";
    >> }
    >> function isArray(o) {
    >> // If these conditions aren't met, it certainly isn't an Array
    >> if (o==null || typeof(o)!="object" || typeof(o.length)!="number") {
    >> return false;
    >> }
    >> // Check to see if the object is an instance of the window's Array
    >> object
    >> if (defined(Array) && defined(o.constructor) && o.constructor==Array) {
    >> return true;
    >> }
    >> // It might be an array defined from another window object - check to
    >> see if it has an Array's methods
    >> if (typeof(o.join)=="function" && typeof(o.sort)=="function" &&
    >> typeof(o.reverse)=="function") {
    >> return true;
    >> }
    >> // As a last resort, let's see if index [0] is defined
    >> return (o.length==0 || defined(o[0]));
    >> };
    >>
    >> Suggestions?

    >
    > var myArray = new Array();
    > myArray[0] = "Its an array";
    > myArray[1] = "It really is an array!";
    > alert(isArray('myArray'))
    >
    > IE7: false
    > Opera 9: false
    > Firefox 2.0: false


    And now I know why after giving it some thought and thinking about it. I
    passed the name of the array rather than the array itself. Might be
    something to think about though. The possibility of passing the name
    instead of the array itself.

    --
    Randy
    Chance Favors The Prepared Mind
    comp.lang.javascript FAQ - http://jibbering.com/faq
    Javascript Best Practices - http://www.JavascriptToolbox.com/bestpractices/
    Randy Webb, Dec 15, 2006
    #9
  10. Matt Kruse wrote:

    > Julian Turner wrote:
    > > Could you could do a toString instead on the constructor, and check
    > > that?

    >
    > I don't think so, because that can vary between browsers and doesn't return
    > just a single string like "Array".


    I take your point. I think it has been noted before that the results
    of toString are implementation independent. However, as you are
    testing for a positive, it could be another String to the bow (if you
    will pardon the pun!).


    [snip]
    > > I note also that "arguments" in IE has "Object" as its constructor,
    > > but "Array" in Opera or Firefox I think.

    >
    > The provided functions works in both cases.


    Yes, it would, as you are testing for length etc.

    > I guess one thing I'm also looking for is something that is most certainly
    > NOT an array, but returns true with the provided function. Then, depending
    > on if the test case is practical or not, I can re-think it. I don't think a
    > perfect solution exists.


    Hmm, what about this.

    function falseArray()
    {
    this.length = 1;
    this[0] = "False first element";
    }

    Can't think when you would encounter this though.

    Regards

    Julian
    Julian Turner, Dec 15, 2006
    #10
  11. Matt Kruse

    VK Guest

    Matt Kruse wrote:
    > I'm seeking the most robust and backwards-compatible (ie, no instanceof)
    > isArray function.


    This sentence contains two mutually exclusive demands, because the most
    robust and backwards-compatible way is (someObj instanceof Array) //
    true / false

    So the max you can ask is "let lesser robust and lesser
    backwards-compatible but without instanceof" and this pretty much
    renders the problem from a practical task into a mind game.

    Also from the posted sample it is hard to say what are you looking for:

    1) to determine if some object has JavaScript Array object in its
    prototype chain (thus inherits the functionality of JavaScript Array).

    2) to determine if some object implementing JavaScript Array
    functionality by its own custom means.

    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).

    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 at the moment of
    creation ("new SomeObject" call) and it is only side-connected (or more
    often not connected at all) to the involved function-constructors. This
    way [constructor] property check is irrelevant to the prototype matter.
    I'm really disappointed to see *such* [constructor] usage in *your*
    code. 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>

    The created prototype chain is immutable and it is the only robust and
    natural way to check the object kind in JavaScript.

    Note:
    On Netscape and Gecko platforms it is possible to hack the chain using
    the proprietary __proto__ property. It means then that someone is
    actively preventing the object's determination by a 3rd party. Despite
    technically possible, it is supported only on a relatively small amount
    of UAs and it is more related with some "conspiracy theory" than with
    any practical programming. At least it has no practical interest to me.

    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.

    instanceof operator is supported at least back to JavaScript 1.2
    (Netscape 4.x) and JScript 5.0 (IE 5.0). This way I don't understand
    what "backwards-compatible" issues could it possibly raise.

    At the same time I know that there is some "opposition" to instanceof
    usage in JavaScript. 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"
    :). This is a silly allusion because instanceof in JavaScript and say
    instanceof in Java share nothing but their names.

    instanceof in JavaScript is in fact a "syntactic sugar" for
    obj.prototype.isPrototypeOf(instance)

    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) 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 pseudo-"double nature" of JavaScript
    objects. Some of them even consider it as some "failure" of instanceof
    operator (to a great fun of anyone reading such statements).

    To sum the things up, instanceof operator is the only one really
    reliable object check tool specially made for it. It must be always
    used - 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.

    Note:
    There can be DOM object with such functionality if it has the needed
    behavior/binding attached.

    While the answer itself is very simple, the question of why such object
    is not possible may need some more explanations.

    There are "fields" and "compound properties". 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.

    Now how is it all connected to a possible Array mimicking? The deal is
    that the [length] is the *only one* compound property existing in the
    basic JavaScript, everything else are fields.

    Note:
    Not all of authors do always understand the unique status of [length]
    as the only one compound property in the entire language. 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.

    Moreover basic JavaScript does *not* allow to create new compound
    properties. The [length] is only one and there cannot be any others
    (again: in the basic scripting environment).

    Now it becomes clear why there cannot be any "self-made" arrays:
    because there is no way to implement the [length] property. 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';
    ret = (len < obj.length);
    delete obj[len];
    }
    return ret;
    }
    There is only one object in JavaScript physically ever capable to pass
    the above check (where setting one property automatically reflected in
    another property). This object will be either an array instance or an
    object instance having Array.prototype somewhere in its prototype
    chain.

    At the same time I want to stress once again that this *tedious* and
    *ridiculous* isArray check is not needed. JavaScript has specially made
    instanceof operator for 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.

    It is possible of course to make a function like
    function isArray(obj) {
    return (obj instanceof Array);
    }
    and then later:
    if (isArray(obj)) {
    // make stuff
    }
    but it's getting too much similar to the infamous Java's trutherizer
    :), so I'm insistently discouraging anyone to go this way.

    Note:
    About the trutherizer (sadly enough based on a real life case) see for
    instance
    <http://mindprod.com/jgloss/unmainobfuscation.html>, sample 34
    Actually the whole page as well as the whole section
    <http://www.mindprod.com/jgloss/unmain.html> is a must-read for any
    programmer, irrelevant of that programming language is being used.

    To make a long story shorter: the only practical check in array could
    be for the presence of particular methods.

    Note:
    It does not affect your particular case, but it has to be kept in mind.
    While Gecko-based and many other browsers report type of native methods
    as "function", Internet Explorer reports type of native methods for
    JScript objects as "function" but type of native methods for DOM
    objects as "object". Say typeof arrayObject.push will be "function" but
    document.createElement will be "object" on IE. As I said it doesn't
    affect you code but it has to be always kept in mind to avoid wrong
    negatives.

    So:
    1) there is no need for isArray method in JavaScript unless you need to
    obfuscate your code. Simply check
    if (someObject instanceof Array) {
    // needed actions
    }
    2) There can be a need to check for a particular method support, but
    naturally it has to go in the head section for the Array prototype
    itself.
    function prepareEnvironment() {
    if (typeof Array.prototype.push != 'function') {
    Array.prototype.push = myCustomMethod;
    }
    // other preparative actions
    }
    VK, Dec 15, 2006
    #11
  12. Matt Kruse

    Randy Webb Guest

    VK said the following on 12/15/2006 11:39 AM:
    > Matt Kruse wrote:
    >> I'm seeking the most robust and backwards-compatible (ie, no instanceof)
    >> isArray function.

    >
    > This sentence contains two mutually exclusive demands


    No it doesn't.

    --
    Randy
    Chance Favors The Prepared Mind
    comp.lang.javascript FAQ - http://jibbering.com/faq
    Javascript Best Practices - http://www.JavascriptToolbox.com/bestpractices/
    Randy Webb, Dec 15, 2006
    #12
  13. Matt Kruse

    VK Guest

    > >> I'm seeking the most robust and backwards-compatible (ie, no instanceof)
    > >> isArray function.

    > >
    > > This sentence contains two mutually exclusive demands

    >
    > No it doesn't.


    Cheking my math :) :

    1) "most robust"
    2) "no instanceof"
    VK, Dec 15, 2006
    #13
  14. Matt Kruse

    VK Guest

    VK wrote:
    > > >> I'm seeking the most robust and backwards-compatible (ie, no instanceof)
    > > >> isArray function.
    > > >
    > > > This sentence contains two mutually exclusive demands

    > >
    > > No it doesn't.

    >
    > Cheking my math :) :
    >
    > 1) "most robust"
    > 2) "no instanceof"


    OK, if we want a IE 4.x or Netscape 3.x compatible solution, that the
    "most robust" way will be the one with [length] autoupdate check (see
    my first post in this thread).
    But as I pointed in advance, it is a concept demonstration, so I'm
    strictly opposed to see this code practically used anywhere, instead of
    the conventional instanceof operator.
    VK, Dec 15, 2006
    #14
  15. Matt Kruse

    Randy Webb Guest

    VK said the following on 12/15/2006 12:11 PM:
    >>>> I'm seeking the most robust and backwards-compatible (ie, no instanceof)
    >>>> isArray function.
    >>> This sentence contains two mutually exclusive demands

    >> No it doesn't.

    >
    > Cheking my math :) :
    >
    > 1) "most robust"
    > 2) "no instanceof"


    As I know from reading 14 kazillion of your posts that English is not
    your primary language I can see where you might be tempted to apply some
    JRS logic to the statement. It is a combined requirement, not exclusive
    requirements.

    --
    Randy
    Chance Favors The Prepared Mind
    comp.lang.javascript FAQ - http://jibbering.com/faq
    Javascript Best Practices - http://www.JavascriptToolbox.com/bestpractices/
    Randy Webb, Dec 16, 2006
    #15
  16. VK wrote:
    > Matt Kruse wrote:
    >> I'm seeking the most robust and backwards-compatible
    >> (ie, no instanceof) isArray function.

    >
    > 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

    > at


    After.

    > the moment of creation


    - of the instance of a new native ECMAScript object.

    > ("new SomeObject" call)


    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.

    <snip>
    > 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 must be always used


    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.

    <snip>
    > 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.
    Richard Cornford, Dec 16, 2006
    #16
  17. Matt Kruse

    Randy Webb Guest

    Richard Cornford said the following on 12/16/2006 1:40 AM:
    > VK wrote:
    >> Matt Kruse wrote:
    >>> I'm seeking the most robust and backwards-compatible
    >>> (ie, no instanceof) isArray function.


    <snip>

    > 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.


    The intent, to me, was very apparent in that the function is
    specifically intended to determine if an object is an array or not and
    do it in the most robust way possible while retaining as much backwards
    compatibility as possible.

    Or, do you expect to get a dissertation from anyone asking a question?

    --
    Randy
    Chance Favors The Prepared Mind
    comp.lang.javascript FAQ - http://jibbering.com/faq
    Javascript Best Practices - http://www.JavascriptToolbox.com/bestpractices/
    Randy Webb, Dec 16, 2006
    #17
  18. Randy Webb wrote:
    > Richard Cornford said the following on 12/16/2006 1:40 AM:
    >> VK wrote:
    >>> Matt Kruse wrote:
    >>>> I'm seeking the most robust and backwards-compatible
    >>>> (ie, no instanceof) isArray function.

    >
    > <snip>
    >
    >> 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.

    >
    > The intent, to me, was very apparent in that the function is
    > specifically intended to determine if an object is an array or not

    <snip>

    That was my impression too, but there was no specification of what being
    an array means in this context. And that question is rendered more
    unclear by Matt's subsequent suggestion that - arguments - objects
    should be included (even though it does not have the methods or an
    array, the special [[Put]] method and assignments to 'array index'
    properties of an - arguments - object are specified as having side
    effects on named properties of the Activation/Variable object for which
    it is the - arguments - value).

    Richard.
    Richard Cornford, Dec 16, 2006
    #18
  19. Matt Kruse

    webEater Guest

    Matt Kruse schrieb:

    > I'm seeking the most robust and backwards-compatible (ie, no instanceof)
    > isArray function.
    >
    > Here's what I have:
    >
    > function defined(o) {
    > return typeof(o)!="undefined";
    > }
    > function isArray(o) {
    > // If these conditions aren't met, it certainly isn't an Array
    > if (o==null || typeof(o)!="object" || typeof(o.length)!="number") {
    > return false;
    > }
    > // Check to see if the object is an instance of the window's Array object
    > if (defined(Array) && defined(o.constructor) && o.constructor==Array) {
    > return true;
    > }
    > // It might be an array defined from another window object - check to see
    > if it has an Array's methods
    > if (typeof(o.join)=="function" && typeof(o.sort)=="function" &&
    > typeof(o.reverse)=="function") {
    > return true;
    > }
    > // As a last resort, let's see if index [0] is defined
    > return (o.length==0 || defined(o[0]));
    > };
    >
    > Suggestions?
    >
    > --
    > Matt Kruse
    > http://www.JavascriptToolbox.com
    > http://www.AjaxToolbox.com


    I am impressed by your discussion, but what I am using and what is
    working an all current browsers is:

    isArray = function(object) {
    return object instanceof Array;
    };
    webEater, Dec 16, 2006
    #19
  20. Matt Kruse

    webEater Guest

    webEater schrieb:

    > Matt Kruse schrieb:
    >
    > > I'm seeking the most robust and backwards-compatible (ie, no instanceof)
    > > isArray function.
    > >
    > > Here's what I have:
    > >
    > > function defined(o) {
    > > return typeof(o)!="undefined";
    > > }
    > > function isArray(o) {
    > > // If these conditions aren't met, it certainly isn't an Array
    > > if (o==null || typeof(o)!="object" || typeof(o.length)!="number") {
    > > return false;
    > > }
    > > // Check to see if the object is an instance of the window's Array object
    > > if (defined(Array) && defined(o.constructor) && o.constructor==Array) {
    > > return true;
    > > }
    > > // It might be an array defined from another window object - check to see
    > > if it has an Array's methods
    > > if (typeof(o.join)=="function" && typeof(o.sort)=="function" &&
    > > typeof(o.reverse)=="function") {
    > > return true;
    > > }
    > > // As a last resort, let's see if index [0] is defined
    > > return (o.length==0 || defined(o[0]));
    > > };
    > >
    > > Suggestions?
    > >
    > > --
    > > Matt Kruse
    > > http://www.JavascriptToolbox.com
    > > http://www.AjaxToolbox.com

    >
    > I am impressed by your discussion, but what I am using and what is
    > working an all current browsers is:
    >
    > isArray = function(object) {
    > return object instanceof Array;
    > };


    I mean IE 5.5+, Opera 7+, Mozilla, FF 1+, Safari 1/2, ..
    webEater, Dec 16, 2006
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Martin Smith

    robust iterator implementation

    Martin Smith, Jan 24, 2005, in forum: C++
    Replies:
    4
    Views:
    4,061
    msalters
    Jan 25, 2005
  2. dorayme

    Robust CSS

    dorayme, Jun 20, 2006, in forum: HTML
    Replies:
    10
    Views:
    817
    dorayme
    Jun 20, 2006
  3. Erik Max Francis
    Replies:
    0
    Views:
    266
    Erik Max Francis
    Jul 9, 2003
  4. Laphan
    Replies:
    11
    Views:
    308
    StephenMcC
    Nov 22, 2004
  5. Aaron Gray

    Its the isArray() function thing again

    Aaron Gray, Jul 26, 2008, in forum: Javascript
    Replies:
    18
    Views:
    201
    dhtml
    Jul 28, 2008
Loading...

Share This Page