Как эффективно проверить, является ли переменная массивом или объектом (в NodeJS и V8)?

Есть ли способ эффективно проверить, является ли переменная объектом или массивом в NodeJS и V8?

Я пишу модель для MongoDB и NodeJS, и для обхода дерева объектов мне нужно знать, является ли объект простым (число, строка,...) или составным (хэш, массив).

Кажется, что V8 имеет быстрый встроенный Array.isArray, но как проверить, является ли объект объектом? Я имею в виду сложный объект, такой как хэш {} или экземпляр класса, а не что-то вроде new String()?

Обычно это можно сделать так:

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

или это:

object === Object(object)

Но, похоже, эта операция стоит недешево, может, есть что-то поэффективнее? Ничего страшного, если он не универсален и не работает на всех двигателях, мне он нужен только для работы с V8.


person Alex Craft    schedule 12.01.2012    source источник
comment
Спасибо за помощь, кстати сама модель здесь alexeypetrushin.github. com/монго-модель/презентации/   -  person Alex Craft    schedule 04.04.2012
comment
Обе эти операции должны быть довольно дешевыми. Если вы хотите узнать, можно ли что-то использовать в качестве объекта (можно получить/установить свойства и т. д.), используйте x === Object(x), но если вы хотите более детальное тестирование, используйте Object.prototype.toString.call(x). Не беспокойтесь о скорости ни того, ни другого, предварительно не профилировав его.   -  person Pauan    schedule 04.02.2013
comment
Есть ли причина, по которой typeof x — плохой выбор? Он вернет «строку» для строки и «объект» для объекта.   -  person JD.    schedule 27.01.2015
comment
typeof [] возвращает "object"   -  person xiix    schedule 10.07.2015


Ответы (12)


Все объекты являются экземплярами как минимум одного класса — Object — в ECMAScript. Вы можете различать экземпляры встроенных классов и обычных объектов только с помощью Object#toString. Все они имеют одинаковый уровень сложности, например, независимо от того, созданы ли они с помощью оператора {} или оператора new.

Object.prototype.toString.call(object) лучше всего подходит для различения обычных объектов и экземпляров других встроенных классов, так как object === Object(object) здесь не работает. Однако я не вижу причин, по которым вам нужно делать то, что вы делаете, поэтому, возможно, если вы поделитесь вариантом использования, я могу предложить немного больше помощи.

person Andy E    schedule 12.01.2012
comment
Спасибо за помощь, модели не содержат схем и могут быть вложены друг в друга, а для выполнения проверок и преобразований мне нужно пройти по этому дереву объектов (без схемы). - person Alex Craft; 12.01.2012
comment
@AlexeyPetrushin: так, по-видимому, существует опасность того, что некоторые экземпляры класса будут переданы вашей функции обхода? Возможно, стоит отметить, что все нативные объекты и их нативные свойства/методы не являются перечисляемыми, что означает, что вы все равно не сможете их обойти. - person Andy E; 12.01.2012

Для простой проверки объекта или массива без дополнительного вызова функции (скорость).

массив()

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 = 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
person zupa    schedule 17.05.2013
comment
Почему бы не использовать return (a)&&(a.constructor === Array)? @зупа - person boxed__l; 16.07.2016
comment
Из того, что я могу проверить: (a) && (a.constructor === Array) не будет работать, если a=null... - person boxed__l; 16.07.2016
comment
Верно, если a равно undefined или null, то будет возвращено undefined или null. Хотя они являются ложными значениями и будут работать в операторах if... в случае примера undefined или null будут зарегистрированы вместо true или false. - person Jorge Cabot; 01.08.2017
comment
Это не удастся, если мы создадим объект из определенной пользователем функции-конструктора. например, var user = new User(). в этом сценарии user.constructor будет пользователем, а не объектом - person Ravindra Thorat; 09.10.2018

Если дело просто в том, чтобы определить, имеете ли вы дело с Object, я мог бы подумать о

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

Однако это, вероятно, не удастся для необъектных примитивных значений. На самом деле нет ничего плохого в вызове .toString() для получения свойства [[cclass]]. Вы даже можете создать хороший синтаксис, например

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

а затем используйте его как

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

Это может быть не самая быстрая операция, но я не думаю, что утечка производительности там слишком велика.

person jAndy    schedule 12.01.2012
comment
+1, хотя bind на самом деле делает его намного медленнее, и это позор, потому что кажется, что изнутри это можно было бы оптимизировать намного лучше. - person Andy E; 12.01.2012
comment
Они довольно быстрые, но все же в 10 раз медленнее, чем Array.isArray. - person Esailija; 12.01.2012
comment
@AndyE: что происходит, я не знал, что связанный метод почти на 30% медленнее jsperf.com/bound-object-prototype-tostring-vs-unbound. Любое объяснение? Esailija: правда, isArray не может победить, но я думаю, что OP хотел явно проверить экземпляр объекта. - person jAndy; 12.01.2012
comment
@jAndy: Хотел бы я знать, я могу только предполагать и надеяться, что это плохо реализовано и неоптимизировано в большинстве движков JS. Если это так, то, вероятно, в какой-то момент он будет улучшен. - person Andy E; 12.01.2012

underscore.js использует следующее

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]';
  };
person Lordking    schedule 04.04.2012
comment
Не будет ли Object(obj) клонировать весь объект? А если очень большой? Мне кажется ужасный способ проверки! - person Eloff; 14.01.2013
comment
Возможно вместо obj === Object(obj); можно было бы obj.constructor === Object.prototype.constructor - person Alex Yaroshevich; 29.01.2013
comment
@Eloff: Нет, согласно спецификации ECMAScript 5, вот что происходит, если вы вызываете Object для чего-то, что уже является объектом: The result is the input argument (no conversion). Вот почему это === для исходного объекта. Так что это должно быть очень быстро. - person Pauan; 04.02.2013
comment
@Алекс Ярошевич: Это неправильно. Это вернет false для всего, что не является прямым экземпляром Object, что означает, что он возвращает false для массивов, функций, регулярных выражений, пользовательских объектов и т. д. - person Pauan; 04.02.2013

Привет, я знаю, что эта тема устарела, но есть гораздо лучший способ отличить массив в Node.js от любого другого объекта, взгляните на документы.

var util = require('util');

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

var obj = {};
typeof obj === "Object" // true
person DeMeNteD    schedule 29.09.2013
comment
Это устарело в Node.js - person Pål Thingbø; 06.07.2016
comment
Да, так как Node 5+ имеет доступ ко многим стандартизированным функциям es6, таким как Array.isArray(obj). developer.mozilla.org/en-US/ документы/Интернет/JavaScript/Справочник/ - person DeMeNteD; 13.07.2016

Я использую typeof, чтобы определить, является ли переменная, на которую я смотрю, объектом. Если это так, я использую instanceof, чтобы определить, какой это вид

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
person DanielS    schedule 12.01.2012
comment
Я заметил, что кто-то только что пришел и проголосовал за все ответы. Кто-нибудь хочет объяснить, почему? - person DanielS; 12.01.2012
comment
Поскольку этот вопрос предназначен специально для V8 и NodeJ, то на самом деле это лучший (правильный) ответ на вопрос... Используйте instanceof Array - person Soren; 19.08.2012
comment
typeof возвращает объект для null, и я уверен, что у него есть другие проблемы. - person robocat; 10.09.2012
comment
null технически правильный способ представления отсутствующего объекта. На мой взгляд, наличие typeof null == 'object' на самом деле прекрасно. Таким же образом typeof NaN == 'number' и typeof undefined == 'undefined'. - person DanielS; 11.09.2012
comment
Будьте осторожны при использовании typeof, потому что typeof new String("foo") вернет "object" вместо "string". То же самое касается typeof null, typeof new Number(), typeof new Boolean() и т. д. Все они возвращают "object". - person geofflee; 16.01.2014
comment
Ну да, но технически (с точки зрения javascript) это объекты :) Если вы хотите проверить тип объекта, используйте instanceof -> new String("foo") instanceof String вернет true. - person DanielS; 16.01.2014

Только что нашел быстрое и простое решение для определения типа переменной.

ES6

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

ES5

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

Примеры:

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

ИЗМЕНИТЬ

Улучшение для предотвращения «неопределенных» и «нулевых» значений:

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());
}
person jlcv    schedule 04.04.2017
comment
Нравится, как это чисто - person Neil Morgan; 15.07.2020

Если вы знаете, что параметр определенно будет либо массивом, либо объектом, может быть проще проверить наличие массива по сравнению с проверкой объекта с чем-то вроде этого.

function myIsArray (arr) {
    return (arr.constructor === Array);
}
person weaVaer    schedule 11.07.2017

Лучший способ, которым я могу использовать свой проект. Используйте hasOwnProperty хитрым способом!.

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)
person VIJAY P    schedule 23.08.2016

глядя на jQuery, они там jQuery.isArray(...) делают:

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

это приводит нас к: jQuery.type:

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

и опять приходится заглядывать: 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();
});

и в нативном JS:

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

это заканчивается:

var isArray = Array.isArray || function( obj ) {
    return toString.call(obj) === "[object Array]";
}
person andlrc    schedule 12.01.2012


Я использовал эту функцию для решения:

function isArray(myArray) {
    return myArray.constructor.toString().indexOf("Array") > -1;
}
person Bernard Doci    schedule 27.04.2015