53

So i have a function that needs to check if an argument is an object, but this fails because:

typeof [] // returns 'object'

This is a classic javascript gotcha, but i cant remember what to do to actually accept objects, but not arrays.

hojberg
  • 1,074
  • 2
  • 9
  • 12
  • 2
    duplicate: http://stackoverflow.com/questions/1202841/what-is-the-best-way-to-check-if-an-object-is-an-array-or-not-in-javascript – Lachlan Roche Feb 15 '10 at 12:46

10 Answers10

57

Try something like this :

obj.constructor.toString().indexOf("Array") != -1

or (even better)

obj instanceof Array
Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
Tomas Vana
  • 18,317
  • 9
  • 53
  • 64
  • it seems your first suggestion is the most safe one and would also fix the problem stated by @Pointy below. Thank you :) – hojberg Feb 15 '10 at 13:04
  • or to be more exact: Object.prototype.toString.call(obj) === '[object Array]'; – hojberg Feb 15 '10 at 13:05
  • 1
    jQuery has a utility method `isArray()`. http://api.jquery.com/jQuery.isArray/ @hojberg I back your way of doing it. jQuery Internally does the same. – Teja Kantamneni Feb 15 '10 at 14:12
  • 3
    This approach will fail if `obj` was created in a different frame. `Array.isArray` doesn't have that issue if you can target ES5. – Joel Mueller Aug 11 '16 at 22:49
  • 1
    I don't believe this answers the question, which is about detecting if an argument is an object. The asker is pointing out that an array creates a false positive when using `typeof`. I've posted an updated answer that should handle either case. – IliasT Jun 15 '17 at 00:00
  • Tell please, what kind of object exist else? Null, Object (not Array), Object Array... what else are they? – Pax Beach Apr 19 '18 at 16:03
  • The "event better" suggestion isn't right `const x = []; x instanceof Object // true` – png Jan 08 '20 at 23:29
23

All these answers suggesting that you check to see (one way or another) if an object is an instance of the "Array" class (that is, constructed by "Array") are really not safe solutions. They'll work sometimes, maybe most of the time, but all the major frameworks have moved away from that approach. One of the main problems with it comes about when there's interaction between multiple windows (generally, a parent window and one or more frame or iframe windows). If you pass an array object created in one window into an API resident in another window, all these tests will fail. Why? Because what you're testing is whether an object is an instance of the "Array" class in your local window context. In other words, when you reference "Array" in

if (myobject instanceof Array) { ... }

what you're referencing is window.Array, of course. Well, an array constructed in another window is not going to be an instance of the Array class in your window!

Checking the constructor name is probably a little safer, though it's still risky. In my opinion, you're better off taking a duck-typing approach. That is, instead of asking, "Is this an Array?" ask instead, "does this object seem to support some particular set of Array APIs I need in this circumstance?" For example, "does this object have a length property?" Javascript is a pretty "soft" language, and just about everything's mutable. Thus even if you do find out something was constructed by "Array", you still really don't know for sure what you can do with it or to it.

[edit] Thanks for that link, @Lachlan - here's a very clear description of the issues: http://juhukinners.com/2009/01/11/typeof-considered-useless-or-how-to-write-robust-type-checks/

Pointy
  • 405,095
  • 59
  • 585
  • 614
23

To test if something is an instance of an Array:

const arr = [1,2,3];
Array.isArray(arr);  // true

To test is something is an instance of an Object:

const obj = { 1: 'a', 2: 'b', 3: 'c' };
obj.constructor === Object;  // true

Note the latter would throw an error if obj is null or undefined, in that case you could use: typeof obj === 'object' or just do a null check: obj?.constructor === Object.

IliasT
  • 3,973
  • 1
  • 24
  • 26
  • In cases like this, you can use the safe-navigation/optional-chaining operator: `obj?.constructor` – Mike Jun 04 '22 at 16:23
11

To determine whether a given object is an array, ECMAScript 5 introduces the Array.isArray() method, which is currently supported across all modern browsers. Refer to this ECMAScript compatibility table.

To determine the class of a particular object, you can use the Object.prototype.toString() method.

Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call([]); // "[object Array]"
Arley
  • 947
  • 1
  • 13
  • 19
5

For what it's worth, here is how jQuery checks whether something is an array:

isArray: function( arr ) {
    return !!arr && arr.constructor == Array;
}

But, this article recommends doing it like this:

function isArray(o) {
    return Object.prototype.toString.call(o) === '[object Array]';
}
Andrew Hedges
  • 21,688
  • 16
  • 67
  • 79
2

Have you tried this:

var test = [];
if(test instanceof Array) {
 ...
}

EDIT: This method doesn't work in multi-frame DOM environments (‘typeof’ considered useless - or how to write robust type checks). (via Pointy)

Arnold Zokas
  • 8,306
  • 6
  • 50
  • 76
  • Not really a good solution. See http://juhukinners.com/2009/01/11/typeof-considered-useless-or-how-to-write-robust-type-checks/ – Pointy Feb 15 '10 at 13:07
  • Your link is defekt (I can't say if by accident or by intent) – mrh1997 Apr 01 '22 at 10:09
0

Look this package

Verify if a given object is not an array for old browsers

https://www.npmjs.com/package/notisarray

JoeLoco
  • 2,116
  • 4
  • 31
  • 59
0

var obj = {first: 'Stack', last: 'Overflow'};
// var obj = ['Stack', 'overflow']; //You can uncomment this line and comment the above to check..

if(Object.prototype.toString.call(obj) !== '[object Array]') {
    //your code..
    var str = '';
    for(var k in obj) {
     str = str + obj[k] + ' ';
    }
    console.log('I love ', str);
    alert('I love ' + str);
} else {
 console.log('Could not process. Probably an array');
  alert('Could not process. Probably an array');
}

console.log('Length is: ', Object.keys(obj).length);
alert('Length is: ' + Object.keys(obj).length);

Let input be an Array or an Object

To check if an object is an Array
if(Object.prototype.toString.call(input) === '[object Array]') {}

To check if an object is an Object
if(Object.prototype.toString.call(input) === '[object Object]') {}


Please note Object.keys(input).length will return you the length for both Array and Object.

JS Fiddle example for the same

Anish Nair
  • 3,238
  • 29
  • 41
0

please use Object.prototype.toString.call({}).slice(8,-1)==="Object" this works for all datatype you can replace the parameter inside call function and the comparison also to check for different datatypes. it works for null check as well

Joey Dias
  • 82
  • 4
0

There are several answers, and most of them correct. I want to recompile:

To ensure that it is not an array:

assert(typeof myVar === 'object' && !Array.isArray(myVar), 'myVar should be an object and not array');

But what about Map, Set and other system iteration objects? We can test this way, and be sure that it is not an iteration object.

assert(typeof myVar === 'object' && !(Symbol.iterator in myVar), 'myVar should be an object and not iterable');
  • the bad part of this, that if you have your own custom class with iteration, this assert will fail on it

As an alternative you can filter by only Classes (objects) that you are expecting to receive:

const approved = [MyClass.constructor.name, MyAnotherClass.constructor.name];
assert(approve.indexOf(myVar.constructor.name) > -1, 'myVar should be one of the ...');
Kostanos
  • 9,615
  • 4
  • 51
  • 65