¿La forma más precisa de verificar el tipo de objeto JS?

138

El typeofoperador realmente no nos ayuda a encontrar el tipo real de un objeto.

Ya he visto el siguiente código:

Object.prototype.toString.apply(t)  

Pregunta:

¿Es la forma más precisa de verificar el tipo de objeto?

Royi Namir
fuente
2
Eche un vistazo a este artículo: javascriptweblog.wordpress.com/2011/08/08/…
James Allardice
44
Mira esta publicación: stackoverflow.com/questions/332422/…
isJustMe
3
La forma más precisa es ... no probar el tipo. ¿Por qué necesitas los tipos?
hugomg
Object.prototype.toString.call / Object.prototype.toString.apply
xgqfrms

Respuestas:

191

La especificación de JavaScript proporciona exactamente una forma adecuada de determinar la clase de un objeto:

Object.prototype.toString.call(t);

http://bonsaiden.github.com/JavaScript-Garden/#types

kmatheny
fuente
55
Si está buscando un tipo específico, es probable que desee hacer algo similar a: lo Object.prototype.toString.call(new FormData()) === "[object FormData]"que sería cierto. También puede usar slice(8, -1)para regresar en FormDatalugar de[object FormData]
Chris Marisic
44
¿Hay alguna diferencia entre usar Object.prototypey {}?
GetFree
3
tal vez esto ha cambiado a lo largo de los años, pero Object.prototype.toString.call(new MyCustomObject())vuelve [object Object]mientras new MyCustomObject() instanceOf MyCustomObject returns trueque es lo que quería (Chrome 54.0.2840.99 m)
Maslow
@ Maslow, me he encontrado con el mismo problema que tú mencionaste. Después de mirar un poco de documentación en línea, terminé usando new MyCustomObject().constructor === MyCustomObject.
solstice333
3
Se plantea la pregunta, ¿por qué no han incluido este código en un método más conveniente o han permitido que un operador adicional compile esto? Sé que solo eres el mensajero, pero francamente eso es horrible.
Andrew S
60

el Object.prototype.toStringes una manera buena, pero su rendimiento es el peor.

http://jsperf.com/check-js-type

comprobar el rendimiento del tipo js

Use typeofpara resolver algún problema básico (String, Number, Boolean ...) y use Object.prototype.toStringpara resolver algo complejo (como Array, Date, RegExp).

y esta es mi solución:

var type = (function(global) {
    var cache = {};
    return function(obj) {
        var key;
        return obj === null ? 'null' // null
            : obj === global ? 'global' // window in browser or global in nodejs
            : (key = typeof obj) !== 'object' ? key // basic: string, boolean, number, undefined, function
            : obj.nodeType ? 'object' // DOM element
            : cache[key = ({}).toString.call(obj)] // cached. date, regexp, error, object, array, math
            || (cache[key] = key.slice(8, -1).toLowerCase()); // get XXXX from [object XXXX], and cache it
    };
}(this));

usar como:

type(function(){}); // -> "function"
type([1, 2, 3]); // -> "array"
type(new Date()); // -> "date"
type({}); // -> "object"
wiky
fuente
Esa prueba en jsPerf no es del todo precisa. Esas pruebas no son iguales (pruebas para la misma cosa). Por ejemplo, typeof [] devuelve "objeto", typeof {} también devuelve "objeto", aunque uno es una matriz de objetos y el otro es un objeto Objeto. Hay muchos otros problemas con esa prueba ... Tenga cuidado al mirar jsPerf que las pruebas comparan manzanas con manzanas.
kmatheny
Su typefunción es buena, pero observe cómo funciona en comparación con otras typefunciones. http://jsperf.com/code-type-test-a-test
Progo
18
Estas métricas de rendimiento deben ser moderadas con cierto sentido común. Claro, el prototype.toString es más lento que los demás en un orden de magnitud, pero en el gran esquema de las cosas, toma en promedio unos cientos de nanosegundos por llamada. A menos que esta llamada se use en una ruta crítica que se ejecuta con mucha frecuencia, esto probablemente sea inofensivo. Prefiero tener un código directo que un código que termine un microsegundo más rápido.
David
({}).toString.call(obj)es más lento que Object.prototype.toString jsperf.com/object-check-test77
timaschew
Buena solución Tomé prestada tu función en mi biblioteca :)
Dong Nguyen
19

La respuesta aceptada es correcta, pero me gusta definir esta pequeña utilidad en la mayoría de los proyectos que construyo.

var types = {
   'get': function(prop) {
      return Object.prototype.toString.call(prop);
   },
   'null': '[object Null]',
   'object': '[object Object]',
   'array': '[object Array]',
   'string': '[object String]',
   'boolean': '[object Boolean]',
   'number': '[object Number]',
   'date': '[object Date]',
}

Usado así:

if(types.get(prop) == types.number) {

}

Si está usando angular, incluso puede inyectarlo limpiamente:

angular.constant('types', types);
parlamento
fuente
11
var o = ...
var proto =  Object.getPrototypeOf(o);
proto === SomeThing;

Controle el prototipo que espera que tenga el objeto y luego compárelo con él.

por ejemplo

var o = "someString";
var proto =  Object.getPrototypeOf(o);
proto === String.prototype; // true
Raynos
fuente
¿Cómo es esto mejor / diferente que decir o instanceof String; //true?
Jamie Treworgy
@jamietre porque "foo" instanceof Stringrompe
Raynos
OK, entonces "typeof (o) === 'objeto' && o instanceof SomeObject". Es fácil probar las cadenas. Simplemente parece un trabajo extra, sin resolver el problema básico de tener que saber de antemano lo que está probando.
Jamie Treworgy
Lamento que el fragmento de código no tenga sentido, pero creo que sabes a lo que me refiero, si estás probando cadenas, entonces typeof(x)==='string'úsalas.
Jamie Treworgy
Por cierto, Object.getPrototypeOf(true)falla donde (true).constructorvuelve Boolean.
katspaugh
5

Yo diría que la mayoría de las soluciones que se muestran aquí sufren de ingeniería excesiva. Probablemente la forma más simple de verificar si un valor es de tipo [object Object]es verificar la .constructorpropiedad del mismo:

function isObject (a) { return a != null && a.constructor === Object; }

o incluso más corto con funciones de flecha:

const isObject = a => a != null && a.constructor === Object;

La a != nullparte es necesaria porque uno puede pasar nullo undefinedno puede extraer una propiedad de constructor de ninguno de estos.

Funciona con cualquier objeto creado a través de:

  • el Objectconstructor
  • literales {}

Otra característica interesante es su capacidad de proporcionar informes correctos para las clases personalizadas que utilizan Symbol.toStringTag. Por ejemplo:

class MimicObject {
  get [Symbol.toStringTag]() {
    return 'Object';
  }
}

El problema aquí es que cuando se llama Object.prototype.toStringa una instancia, [object Object]se devolverá el informe falso :

let fakeObj = new MimicObject();
Object.prototype.toString.call(fakeObj); // -> [object Object]

Pero verificar contra el constructor da un resultado correcto:

let fakeObj = new MimicObject();
fakeObj.constructor === Object; // -> false
David
fuente
4

La mejor manera de averiguar el tipo REAL de un objeto (incluyendo AMBOS el nombre del objeto nativo o del tipo de datos (como String, Date, Number, ..etc) Y el tipo REAL de un objeto (incluso los personalizados); es agarrando La propiedad de nombre del constructor del prototipo de objeto:

Tipo nativo Ex1:

var string1 = "Test";
console.log(string1.__proto__.constructor.name);

muestra:

String

Ex2:

var array1 = [];
console.log(array1.__proto__.constructor.name);

muestra:

Array

Clases personalizadas:

function CustomClass(){
  console.log("Custom Class Object Created!");
}
var custom1 = new CustomClass();

console.log(custom1.__proto__.constructor.name);

muestra:

CustomClass
beliha
fuente
Esto falla si el objeto es nullo undefined.
Julian Knight
2

Antigua pregunta que sé. No necesitas convertirlo. Ver esta función:

function getType( oObj )
{
    if( typeof oObj === "object" )
    {
          return ( oObj === null )?'Null':
          // Check if it is an alien object, for example created as {world:'hello'}
          ( typeof oObj.constructor !== "function" )?'Object':
          // else return object name (string)
          oObj.constructor.name;              
    }   

    // Test simple types (not constructed types)
    return ( typeof oObj === "boolean")?'Boolean':
           ( typeof oObj === "number")?'Number':
           ( typeof oObj === "string")?'String':
           ( typeof oObj === "function")?'Function':false;

}; 

Ejemplos:

function MyObject() {}; // Just for example

console.log( getType( new String( "hello ") )); // String
console.log( getType( new Function() );         // Function
console.log( getType( {} ));                    // Object
console.log( getType( [] ));                    // Array
console.log( getType( new MyObject() ));        // MyObject

var bTest = false,
    uAny,  // Is undefined
    fTest  function() {};

 // Non constructed standard types
console.log( getType( bTest ));                 // Boolean
console.log( getType( 1.00 ));                  // Number
console.log( getType( 2000 ));                  // Number
console.log( getType( 'hello' ));               // String
console.log( getType( "hello" ));               // String
console.log( getType( fTest ));                 // Function
console.log( getType( uAny ));                  // false, cannot produce
                                                // a string

Bajo costo y simple.

Codebeat
fuente
Devuelve falsesi el objeto de prueba es nulloundefined
Julian Knight
o trueofalse
Julian Knight
@JulianKnight false está bien en nulo o indefinido, no es nada útil. Entonces, ¿cuál es el punto?
Codebeat
Su ejemplo devuelve datos inconsistentes. Algunos resultados son el tipo de datos y otros son el valor false. ¿Cómo ayuda esto a responder la pregunta?
Julian Knight
1
@JulianKnight Ver cambios, ¿es eso lo que quieres? Si prefiere indefinido o "indefinido" como resultado, puede reemplazar el último falso si lo desea.
Codebeat
0

Reuní una pequeña utilidad de verificación de tipo inspirada en las respuestas correctas anteriores:

thetypeof = function(name) {
        let obj = {};
        obj.object = 'object Object'
        obj.array = 'object Array'
        obj.string = 'object String'
        obj.boolean = 'object Boolean'
        obj.number = 'object Number'
        obj.type = Object.prototype.toString.call(name).slice(1, -1)
        obj.name = Object.prototype.toString.call(name).slice(8, -1)
        obj.is = (ofType) => {
            ofType = ofType.toLowerCase();
            return (obj.type === obj[ofType])? true: false
        }
        obj.isnt = (ofType) => {
            ofType = ofType.toLowerCase();
            return (obj.type !== obj[ofType])? true: false
        }
        obj.error = (ofType) => {
            throw new TypeError(`The type of ${name} is ${obj.name}: `
            +`it should be of type ${ofType}`)
        }
        return obj;
    };

ejemplo:

if (thetypeof(prop).isnt('String')) thetypeof(prop).error('String')
if (thetypeof(prop).is('Number')) // do something
Abraham Juliot
fuente
No parece funcionar para objetos que son nullo undefinedo trueofalse
Julian Knight