69

Is there any way to efficiently check if the variable is Object or Array, in NodeJS & V8?

I'm writing a Model for MongoDB and NodeJS, and to traverse the object tree I need to know if the object is simple (Number, String, ...) or composite (Hash, Array).

It seems that V8 has fast built-in Array.isArray, but how to check if object is an Object? I mean complex object like hash {} or instance of class, not something like new String()?

Usually it may be done as this:

Object.prototype.toString.call(object) == "[object Object]"

or this:

object === Object(object)

But it seems that this operations aren't cheap, maybe there's some more efficient? It's ok if it's not universal and doesn't works on all engines, I need it only to work on V8.

Alex Craft
  • 13,598
  • 11
  • 69
  • 133
  • Thanks for help, by the way the model itself is here http://alexeypetrushin.github.com/mongo-model/presentations/introduction/index.html – Alex Craft Apr 04 '12 at 09:34
  • Both of those operations should be quite cheap. If you want to know if something can be used as an object (can get/set properties, etc.) use `x === Object(x)` but if you want more fine-grained testing, use `Object.prototype.toString.call(x)`. Don't worry about the speed of either one, not without profiling it first. – Pauan Feb 04 '13 at 04:39
  • Is there a reason `typeof x` is a poor choice? It will return 'string' for a string and 'object' for an object. – JD. Jan 27 '15 at 16:24
  • 3
    `typeof []` returns `"object"` – xiix Jul 09 '15 at 21:32
  • Possible duplicate of [Check if a value is an object in JavaScript](http://stackoverflow.com/questions/8511281/check-if-a-value-is-an-object-in-javascript) – Inanc Gumus Dec 14 '16 at 11:22

15 Answers15

132

For simply checking against Object or Array without additional function call (speed).

isArray()

let isArray = function(a) {
    return (!!a) && (a.constructor === Array);
};
console.log(isArray(        )); // false
console.log(isArray(    null)); // false
console.log(isArray(    true)); // false
console.log(isArray(       1)); // false
console.log(isArray(   'str')); // false
console.log(isArray(      {})); // false
console.log(isArray(new Date)); // false
console.log(isArray(      [])); // true

isObject()

let isObject = function(a) {
    return (!!a) && (a.constructor === Object);
};
console.log(isObject(        )); // false
console.log(isObject(    null)); // false
console.log(isObject(    true)); // false
console.log(isObject(       1)); // false
console.log(isObject(   'str')); // false
console.log(isObject(      [])); // false
console.log(isObject(new Date)); // false
console.log(isObject(      {})); // true
zupa
  • 12,809
  • 5
  • 40
  • 39
  • Why not use `return (a)&&(a.constructor === Array)`? @zupa – boxed__l Jul 16 '16 at 07:45
  • 1
    From what I can check: `(a) && (a.constructor === Array)` won't work if `a=null`... – boxed__l Jul 16 '16 at 07:49
  • Right, if `a` is `undefined` or `null` then `undefined` or `null` will be returned. Though they are falsy values and would work in `if` statements... in the case of the example `undefined` or `null` would be logged instead of `true` or `false`. – Jorge Cabot Aug 01 '17 at 20:54
  • 1
    It will fail if we the object is getting created from a user defined constructor function. for example, var user = new User(). in this scenario, user.constructor will be "User" and not "Object" – Ravindra Thorat Oct 09 '18 at 06:02
  • Doesn't work for custom classes – d512 Sep 18 '21 at 21:28
  • Thank you! Here's my ES11 modified shorthand for testing if value is a plain object: `const isObject = (obj) => (obj ?? false)?.constructor?.name === "Object";` Works great in NodeJS. – szegheo Dec 15 '21 at 11:08
  • does not work for `isObject(new Error())` – Switch386 Dec 16 '22 at 15:08
27

All objects are instances of at least one class – Object – in ECMAScript. You can only differentiate between instances of built-in classes and normal objects using Object#toString. They all have the same level of complexity, for instance, whether they are created using {} or the new operator.

Object.prototype.toString.call(object) is your best bet to differentiate between normal objects and instances of other built-in classes, as object === Object(object) doesn't work here. However, I can't see a reason why you would need to do what you're doing, so perhaps if you share the use case I can offer a little more help.

Andy E
  • 338,112
  • 86
  • 474
  • 445
  • Thanks for help, models are schema-free and can be nested - and in order to perform validations and conversions I need to traverse this object tree (without schema). – Alex Craft Jan 12 '12 at 11:44
  • 2
    @AlexeyPetrushin: so, presumably, there's a danger of some class instances being passed to your traversal function? It may be worth noting that all native objects and their native properties/methods are non-enumerable, which means you can't traverse them anyway. – Andy E Jan 12 '12 at 11:56
21

If its just about detecting whether or not you're dealing with an Object, I could think of

Object.getPrototypeOf( obj ) === Object.prototype

However, this would probably fail for non-object primitive values. Actually there is nothing wrong with invoking .toString() to retreive the [[cclass]] property. You can even create a nice syntax like

var type = Function.prototype.call.bind( Object.prototype.toString );

and then use it like

if( type( obj ) === '[object Object]' ) { }

It might not be the fastest operation but I don't think the performance leak there is too big.

jAndy
  • 231,737
  • 57
  • 305
  • 359
  • 1
    +1, though `bind` actually makes it a lot slower, which is a shame because it's something that seems like it could be optimized much better under the hood. – Andy E Jan 12 '12 at 11:29
  • 3
    [They are pretty fast](http://jsperf.com/it-is-cheap), but still 10 times slower than `Array.isArray`. – Esailija Jan 12 '12 at 11:35
  • @AndyE: wut is going on, I wasn't aware that a bound method is almost 30% slower http://jsperf.com/bound-object-prototype-tostring-vs-unbound. Any explanation ? Esailija: true, `isArray` cannot get beat, but I guess OP wanted to check for an object instance explicitly. – jAndy Jan 12 '12 at 11:40
  • @jAndy: I wish I knew, I can only speculate and hope that it is poorly implemented and unoptimized in the majority of JS engines. If that's the case then it will probably be improved at some point. – Andy E Jan 12 '12 at 11:51
15

underscore.js is using the following

toString = Object.prototype.toString;

_.isArray = nativeIsArray || function(obj) {
    return toString.call(obj) == '[object Array]';
  };

_.isObject = function(obj) {
    return obj === Object(obj);
  };

_.isFunction = function(obj) {
    return toString.call(obj) == '[object Function]';
  };
Lordking
  • 1,413
  • 1
  • 13
  • 31
  • WOuldn't Object(obj) clone the entire object? What if it's very large? Seems like a horrible way of checking to me! – Eloff Jan 14 '13 at 04:11
  • Probably instead of `obj === Object(obj);` it could be `obj.constructor === Object.prototype.constructor` – Alex Yaroshevich Jan 29 '13 at 00:52
  • 2
    @Eloff: No, according to the ECMAScript 5 spec, this is what happens if you call `Object` on something that's already an object: `The result is the input argument (no conversion).` That's why it's `===` to the original object. So it should be *very* fast. – Pauan Feb 04 '13 at 04:32
  • @Alex Yaroshevich: That's incorrect. That will return `false` on anything that isn't a direct instance of `Object`, which means it returns `false` on Arrays, Functions, RegExps, custom objects, etc. – Pauan Feb 04 '13 at 04:35
13

I use typeof to determine if the variable I'm looking at is an object. If it is then I use instanceof to determine what kind it is

var type = typeof elem;
if (type == "number") {
    // do stuff
}
else if (type == "string") {
    // do stuff
}
else if (type == "object") { // either array or object
    if (elem instanceof Buffer) {
    // other stuff
DanielS
  • 545
  • 3
  • 10
  • I noticed somebody just came along and downvoted all answers. Anyone care to explain why? – DanielS Jan 12 '12 at 12:58
  • Since this question is specifically for V8 & NodeJs, then this is actually the best (correct) answer afaict.... Use "instanceof Array" – Soren Aug 19 '12 at 01:06
  • typeof returns "object" for null, and I am sure it has other problems. – robocat Sep 10 '12 at 05:54
  • 1
    `null` is technically the correct way to represent a "missing" object. Having `typeof null == 'object'` is actually fine in my opinion. In the same manner `typeof NaN == 'number'` and `typeof undefined == 'undefined'`. – DanielS Sep 11 '12 at 05:55
  • Be careful when using `typeof` because `typeof new String("foo")` will return `"object"` instead of `"string"`. Same goes for `typeof null`, `typeof new Number()`, `typeof new Boolean()`, etc… They all return `"object"`. – geofflee Jan 16 '14 at 03:07
  • Well yes, but technically (javascript wise) those are objects :) If you want to check the object's type, use `instanceof` -> `new String("foo") instanceof String` will return `true`. – DanielS Jan 16 '14 at 14:10
12

Hi I know this topic is old but there is a much better way to differentiate an Array in Node.js from any other Object have a look at the docs.

var util = require('util');

util.isArray([]); // true
util.isArray({}); // false

var obj = {};
typeof obj === "object" // true
kakyo
  • 10,460
  • 14
  • 76
  • 140
DeMeNteD
  • 385
  • 4
  • 11
10

Just found a quick and simple solution to discover type of a variable.

ES6

export const isType = (type, val) => val.constructor.name.toLowerCase() === type.toLowerCase();

ES5

function isType(type, val) {
  return val.constructor.name.toLowerCase() === type.toLowerCase();
}

Examples:

isType('array', [])
true
isType('array', {})
false
isType('string', '')
true
isType('string', 1)
false
isType('number', '')
false
isType('number', 1)
true
isType('boolean', 1)
false
isType('boolean', true)
true

EDIT

Improvment to prevent 'undefined' and 'null' values:

ES6

export const isType = (type, val) => !!(val.constructor && val.constructor.name.toLowerCase() === type.toLowerCase());

ES5

function isType(type, val) {
  return !!(val.constructor && val.constructor.name.toLowerCase() === type.toLowerCase());
}
jlcv
  • 142
  • 1
  • 8
7

If you know that a parameter will definitely be either an array or an object, it may be easier to check for an array compared to checking for an object with something like this.

function myIsArray (arr) {
    return (arr.constructor === Array);
}
Paul Rooney
  • 20,879
  • 9
  • 40
  • 61
weaVaer
  • 131
  • 2
  • 4
3

The Best Way I Can Use My Project.Use hasOwnProperty in Tricky Way!.

var arr = []; (or) arr = new Array();
var obj = {}; (or) arr = new Object();

arr.constructor.prototype.hasOwnProperty('push') //true (This is an Array)

obj.constructor.prototype.hasOwnProperty('push') // false (This is an Object)
VIJAY P
  • 1,363
  • 1
  • 12
  • 14
3

I know it's been a while, but I thought i would update the answer since there are new (faster and simpler) ways to solve this problem. Since ECMAscript 5.1 yo can use the isArray() method avaiable in the Array class.

Yo can see it's documentation in MDN here.

I think you shouldn't have a compatibility problem nowadays, but just in case, if you add this to your code you should be always safe that Array.isArray() is polyfilled:

if (!Array.isArray) {
  Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === '[object Array]';
  };
}
Solano
  • 45
  • 1
  • 8
1

If you're sure that the variable you're checking will be either an object or an array, you could use the length property as well.

variable.length on an array will return an integer between 1 and n whereas variable.length on an object will return undefined.

If you have other data types in the mix then you can add separate checks for those.

jaimish11
  • 536
  • 4
  • 15
1

looking at jQuery they in there jQuery.isArray(...) they do:

    isArray = Array.isArray || function( obj ) {
    return jQuery.type(obj) === "array";
}

this leads us to: jQuery.type:

    type = function( obj ) {
    return obj == null ?
        String( obj ) :
        class2type[ toString.call(obj) ] || "object";
}

and again we have to look in: class2type

class2type = {};

// Populate the class2type map
jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
    class2type[ "[object " + name + "]" ] = name.toLowerCase();
});

and in native JS:

var a, t = "Boolean Number String Function Array Date RegExp Object".split(" ");
for( a in t ) {
    class2type[ "[object " + t[a] + "]" ] = t[a].toLowerCase();
}

this ends up with:

var isArray = Array.isArray || function( obj ) {
    return toString.call(obj) === "[object Array]";
}
Andreas Louv
  • 46,145
  • 13
  • 104
  • 123
0

I've used this function to solve:

function isArray(myArray) {
    return myArray.constructor.toString().indexOf("Array") > -1;
}
Mitro
  • 1,230
  • 8
  • 32
  • 61
Bernard Doci
  • 739
  • 3
  • 14
  • 23
0

Making - Object.isObject()

Object.prototype.isObject = function (param) {
   if(!param){return}
   return param.constructor === Object ?  true : false;
}

Calling - Object.isObject()

Object.isObject({})                       // returns true
Object.isObject([])                       // returns false
Object.isObject("I am string")            // returns false
Abhinash Majhi
  • 499
  • 1
  • 4
  • 16
0

This answer will evaluate inner arrays values:

function cleanObj(obj) {
  for (var propName in obj) {
    if (obj[propName] === null || obj[propName] === undefined) {
      delete obj[propName];
    } else if (Array.isArray(obj[propName])) {
      obj[propName] = obj[propName].map((item) => {
        return cleanObj(item);
      });
    } else if (typeof obj[propName] === 'object') {
      obj[propName] = cleanObj(obj[propName]);
    }
  }
  return obj;
}
Idemax
  • 2,712
  • 6
  • 33
  • 66