简体   繁体   中英

How to efficiently check if variable is Array or Object (in NodeJS & V8)?

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.

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

isArray()

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()

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

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.

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.

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

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

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

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

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

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)

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

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

I've used this function to solve:

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

Just for the record, lodash also has isObject(value)

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM