Infinite Ways to Detect Array in JavaScript

JavaScript (ECMA script in general) is a miraculous language. It allows us to prove that coding is creativity. One reason for that is the numerous ways a single objective can be coded. For instance, checking whether a variable is an Array, can be done in four different ways. And probably more!

Some months back I had come across this Quora article: What’s the best way to tell the different between an Array and other kinds of objects in JavaScript? There were already five different methods available there from Tom, John, Rick, Ken and Eric. And even I was surprised that I could add one more! I would take some time and explain all the methods with its pros and cons and finally we can conclude upon the best method.

The ECMAScript 5 isArray Function

function isArray(obj) {
    return Array.isArray(obj);
}

Undoubtedly, this sounds to be the perfect solution as it is compiler’s native function and is expected to be responsive and reliable. However, this is available only in modern browsers that support ECMAScript 5. Look up the ECMAScript 5 compatibility table and you would be disappointed.

Constructor Checking

function isArray(obj) {
    return (typeof obj !== 'undefined' &&
            obj && obj.constructor === Array);
}

Constructor checking is very fast and very accurate. In fact it is too accurate for use. It fails to identify an variable when it has been inherited from an Array. This is, in a way, is helpful when this is actually what you need.

The instanceof Operator

function isArray(obj) {
    return obj instanceof Array;
}

This should have sounded the best and in fact, can be the best if you do not intend to check variables across frames. This method of detection would fail if you try checking it with respect to a variable created in some other window or inside some other frame (or iframe).

Object’s Prototype toString Check

isArray = function(obj) {
    return Object.prototype.toString.call(obj) == "[object Array]";
}

This is a very slick method. Every (almost every) object has a toString method that converts an object to string. And the string representation of the array constructor is consistent and is useful enough for identification. This method is just good enough to fit into our requirements; works in almost all scenarios.

Duck Check

var isArray = function (arg) {
    if (typeof arg === 'object' &&
            ('join' in arg && typeof arg.join === 'function') &&
            ('length' in arg && typeof arg.length === 'number')) {
        return true;
    }
    return false;
}

Straight from the books: “When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.” An array has a couple of very prominent properties like length, push, join, etc. We test the presence of these (duck test).

This method is not always accurate as it is very easy to fool this. I would rather use it for strict interface checking and not type-detection.

Exception Check

var isArray = function (subj) {
    try {
        subj && (subj.length = -1);
        return false;
    }
    catch (er) {
        return true;
    }
};

As most of my acquaintances had to say, this method wins more points for its creativity (than its actual use!) An array’s length cannot be negative and when forced, that raises an error. All said, the try-catch block is super-slow and would beat the whole purpose of using a fast method of detecting array.

Perhaps the Best Method of Array Detection

The most optimized method is perhaps the use of Array.isArray wherever available and otherwise fall back to constructor string checking.

/**
 * Check whether an object is Array or not
 * @type Boolean
 * @param {object} subject is the variable that is
 * tested for Array identity check
 */
var isArray = (function () {
    // Use compiler's own isArray when available
    if (Array.isArray) {
        return Array.isArray;
    } 

    // Retain references to variables for performance
    // optimization
    var objectToStringFn = Object.prototype.toString,
        arrayToStringResult = objectToStringFn.call([]); 

    return function (subject) {
        return objectToStringFn.call(subject) === arrayToStringResult;
    };
}());

Why is this best?

First, it tries to use the built-in isArray function of browsers that has it and that is definitely pretty fast. For the rest unlucky browsers, we use the toString function of objects to identify an Array.

13 Replies to “Infinite Ways to Detect Array in JavaScript”

  1. This doesn’t address `arguments`
    Often you need to check if a parameter is an array. Hence you can call that function with parameter arguments (the current arguments of the calling function), but the `best method` will fail silently

    1. Right! Arguments object is not a true array. When working with arguments object we would anyways need to have extra caution since its behaviour on array like operations is quirky.

      For arguments to be identified as true for array, we would need to duck check length and callee. That would again “work” but would be unreliable for all other use cases!

      What is your take on the best solution catering to popular use cases?

    1. Polluting a native object’s prototype is not a good practice. If you’ve noticed, in all these checks to see whether isArray is supported by browser, I’ve checked the presence of the function in the object. This way of “feature detection”, instead of the unreliable browser version detection, will not work if everyone starts adding functionality to native browser objects.

      Imagine, you are checking whether window.attachEvent exists or not, in order to add event listeners – your code will break if some other script prior to your script has already added that function – you’ve no reliable way then to check whether that custom addition works correctly.

    1. At what rate do you do this operation in your real-world apps? How significant does it impact your app’s performance in the real world?

      1. This was posted in the year 2011, back when even calling a function was expensive in browsers. Not sure whether you had the opportunity to dabble with arrays at that time. 🙂

        Currently, it does not matter that much. But, mostly, the definition of “real world” is really substandard in Javascript ecosystem. Perhaps when a single instance of your server is designed to receive 1M+ req/hour – which I have worked on, this becomes an issue.

        I did sound condescending. But that was only because you quoted “real world”. Apologies for that.

        1. The parent comment was posted 3 months ago as far as I can see.

          As for 1M+ req/hour, any bottleneck, if any, would likely reside elsewhere in your app than in a array detection, unless you are telling me your app is mostly just doing array detection during a req.

  2. It has been many years since this article was written, and since then ES6 has appeared. 🎉

    And it brings a new failure mode:
    `objectToStringFn.call([]) === arrayToStringResult` could fail if Array.prototype[Symbol.toStringTag] has been defined to something other than “Array” after arrayToStringResult was put into the closure.
    `objectToStringFn.call({}) === arrayToStringResult` could pass (false positive) if Array.prototype[Symbol.toStringTag] has been defined to “Object” before arrayToStringResult was put into the closure.

    I don’t know of a reason why this would happen except by accident, but accidents happen.

Leave a Reply