71

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.

5
  • Thanks for help, by the way the model itself is here alexeypetrushin.github.com/mongo-model/presentations/… Commented Apr 4, 2012 at 9: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. Commented Feb 4, 2013 at 4:39
  • Is there a reason typeof x is a poor choice? It will return 'string' for a string and 'object' for an object. Commented Jan 27, 2015 at 16:24
  • 3
    typeof [] returns "object" Commented Jul 9, 2015 at 21:32
  • Possible duplicate of Check if a value is an object in JavaScript Commented Dec 14, 2016 at 11:22

15 Answers 15

136

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
Sign up to request clarification or add additional context in comments.

8 Comments

Why not use return (a)&&(a.constructor === Array)? @zupa
From what I can check: (a) && (a.constructor === Array) won't work if a=null...
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.
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"
Doesn't work for custom classes
|
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.

2 Comments

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

4 Comments

+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.
They are pretty fast, but still 10 times slower than Array.isArray.
@AndyE: wut is going on, I wasn't aware that a bound method is almost 30% slower 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: 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.
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]';
  };

4 Comments

WOuldn't Object(obj) clone the entire object? What if it's very large? Seems like a horrible way of checking to me!
Probably instead of obj === Object(obj); it could be obj.constructor === Object.prototype.constructor
@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.
@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.
14

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

6 Comments

I noticed somebody just came along and downvoted all answers. Anyone care to explain why?
Since this question is specifically for V8 & NodeJs, then this is actually the best (correct) answer afaict.... Use "instanceof Array"
typeof returns "object" for null, and I am sure it has other problems.
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'.
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".
|
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

2 Comments

This is deprecated in Node.js
Yes since Node 5+ has access to many standardised es6 features like Array.isArray(obj). developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
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());
}

Comments

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);
}

Comments

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)

Comments

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]';
  };
}

1 Comment

Good solution, ECMAscript5.1 come since June 2011, so now almost all users should have a compatible browser
2

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.

Comments

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]";
}

Comments

0

I've used this function to solve:

function isArray(myArray) {
    return myArray.constructor.toString().indexOf("Array") > -1;
}

Comments

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

Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.