July 21, 2011

How to detect a JavaScript array

"Piece of cafe" I bet you're thinking, oh I wish it were that easy.

But fear not, JavaScript does have the typeof operator (returns a string) which is very useful for type-checking:
var str = 'hello',
    num = 64,
    bool = false,
    func = function () {},
    undef;

typeof str; // "string"
typeof num; // "number"
typeof bool; // "boolean"
typeof func; // "function"
typeof undef; // "undefined"
Like shown in the previous code, it works fine for most things. However, it's a little flawed:
var n = null,
    str = new String('hello'), // any wrapper function or custom constructor
    arr = [1, 2, 3];

typeof n; // "object", which is wrong
typeof str; // "object", which is correct but not very useful.
typeof arr; // "object", which is correct but not very useful.

constructor property
Every created object has a constructor property that points to the function that created the object's prototype. You can leverage that reference to do some accurate detections:
var str = new String('hello'),
    arr = [1, 2, 3],
    obj,
    Person = function (name) {
        this.name = name;
    };

obj = new Person('John');

obj.constructor === Person; // true
str.constructor === String; // true
arr.constructor === Array; // true
isArray method
ECMAScript 5 defines a new method for the Array function called isArray(). If it's not implemented by the browser you can define it like this:
if (!Array.hasOwnProperty('isArray')) {
    Array.isArray = function (value) {
        return Object.prototype.toString.call(value) === '[object Array]';
    };
}
Invoking the toString() method of Object, gets us a string representation of the object, for arrays it's [object Array]:
var arr1 = [1, 2, 3],
    arr2 = new Array(1, 2, 3);

Array.isArray(arr1); // true
Array.isArray(arr2); // true
Array.isArray(new Number('12')); // false
Array.isArray({}); // false

Sources:
typeof operator (MDC)
constructor property (MDC)

3 comments:

  1. Your post inspired me to create the following two functions.

    The first, called type, returns 'number', string', null', undefined', 'array', 'regexp', 'boolean' etc according to the type of its argument.

    function type (v) {
    return !v && (v === null || v === undefined) ? '' + v :
    ((v = Object.prototype.toString.call (v)).match (/object (\w+)/) || ['', v])[1].toLowerCase ();
    }

    The second, called argType, takes a single string argument which is the name of an argument of the calling function, and returns that arguments type.

    If the argType argument is not the name of in the callers argument list, it returns '?', if callers named argument was not specified in its invoking call, argType returns ''.

    function argType (a) {
    var c = argType.caller;
    a = c.toString ().match (/function \w*\((.*?)\)/)[1].split (',').indexOf (a);
    return a < 0 ? '?' : c.arguments.length > a ? type (c.arguments[a]) : '';
    }

    Hope these are of interest.

    ReplyDelete
  2. Developing this idea further:

    (function () {
    arguments.constructor.prototype.type = function (a) {
    var c = this.callee;
    return (a = c.toString ().match (/function\s+\w*\s*\((.*?)\)/)[1].split (/\s*,\s*/).indexOf (a)) < 0 ? '?' :
    a >= this.length ? '' :
    !(a = this[a]) && (a === null || a === undefined) ? '' + a :
    ((a = Object.prototype.toString.call (a)).match (/object (\w+)/) || ['', a])[1].toLowerCase ();
    };
    }) ();

    This JavaScript will install new property function called type to the arguments prototype.

    Now you can call arguments.type ('a') which will return the type of the argument called a. The return values are the same as my argType function in my previous comment.

    These were tested on Chrome, Firefox and Opera running under Ubuntu, I'd be interested in knowing if they work on other browsers.

    ReplyDelete
  3. What about the instanceof keyword? Works similarly to typeof but tests the constructor rather than the variable's type:

    [] instanceof Array //true
    {} instanceof Object //true
    var bob = new Person();
    bob instanceof Person //true

    ReplyDelete