¿Mejor manera de obtener el tipo de una variable Javascript?

260

¿Hay una mejor manera de obtener el tipo de una variable en JS que typeof? Funciona bien cuando haces:

> typeof 1
"number"
> typeof "hello"
"string"

Pero es inútil cuando intentas:

> typeof [1,2]
"object"
>r = new RegExp(/./)
/./
> typeof r
"function"

Lo sé instanceof, pero esto requiere que sepas el tipo de antemano.

> [1,2] instanceof Array
true
> r instanceof RegExp
true

¿Hay una mejor manera?

Aillyn
fuente
1
FYI, el typeof new RegExp(/./); // "function"problema en Chrome parece estar solucionado en Chrome 14.
user113716
Advertencia: si está minimizando su JS y está utilizando 'objetos personalizados' como clases de mecanografía, algunas de las respuestas a continuación terminarán proporcionándole el nombre de la función ofuscada, como en nlugar del nombre original esperado. por ejemplo, constructor.namepodría darte en nlugar del nombre completo esperado
Simon_Weaver

Respuestas:

221

Angus Croll escribió recientemente una publicación de blog interesante sobre esto:

http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/

Revisa los pros y los contras de los diversos métodos y luego define un nuevo método 'toType':

var toType = function(obj) {
  return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
ipr101
fuente
3
Nota al margen interesante aquí: esto podría romperse cuando se pasa un objeto "host" a la función. ActiveXObjectInstancias de Internet Explorer , por ejemplo.
Andy E
Si creó su propio tipo de objeto (herencia prototípica), ¿cómo podría lograr que devuelva un valor más específico que "objeto"? Esto también se planteó como un problema aquí , pero nunca se abordó.
EleventyOne
1
Si su nombre de tipo contiene: o _ puede extender esta expresión regular amatch(/\s([a-z,_,:,A-Z]+)/)
masi
66
La expresión regular también necesitaría incluir números para cosas como Uint8Array. En cambio, considere el más general match(/\s([^\]]+)/)que debería manejar cualquier cosa.
Lily Finley
2
Sugeriría usar la \wpalabra operador en su lugar ...
Kaiser
51

Puedes intentar usarlo constructor.name.

[].constructor.name
new RegExp().constructor.name

Al igual que con todo JavaScript, alguien eventualmente señalará invariablemente que esto es de alguna manera malvado, así que aquí hay un enlace a una respuesta que cubre esto bastante bien.

Una alternativa es usar Object.prototype.toString.call

Object.prototype.toString.call([])
Object.prototype.toString.call(/./)
Alex Turpin
fuente
10
namees una extensión de ECMAScript y no funcionará en todos los navegadores.
Andy E
16
La respuesta fue votada debido a la sospecha de que todo lo que se hace en JavaScript es de alguna manera malvado.
liljoshu
1
MAL: si está minimizando su código, constructor.namees probable que termine siendo algo así como nel nombre de objeto que espera. Así que ten cuidado con eso, especialmente si solo minimizas la producción.
Simon_Weaver
nully undefineddesafortunadamente no tengo un constructor.
Andrew Grimm
JavaScript en sí mismo es malo. Y tonto. El hecho de que esta pregunta no tenga una buena respuesta es la prueba viviente de eso.
user2173353
38

Una función de captura de tipo razonablemente buena es la utilizada por YUI3 :

var TYPES = {
    'undefined'        : 'undefined',
    'number'           : 'number',
    'boolean'          : 'boolean',
    'string'           : 'string',
    '[object Function]': 'function',
    '[object RegExp]'  : 'regexp',
    '[object Array]'   : 'array',
    '[object Date]'    : 'date',
    '[object Error]'   : 'error'
},
TOSTRING = Object.prototype.toString;

function type(o) {
    return TYPES[typeof o] || TYPES[TOSTRING.call(o)] || (o ? 'object' : 'null');
};

Esto captura muchas de las primitivas proporcionadas por javascript, pero siempre puede agregar más modificando el TYPESobjeto. Tenga typeof HTMLElementCollectionen cuenta que en Safari informará function, pero el tipo (HTMLElementCollection) devolveráobject

Nick Husher
fuente
31

Puede encontrar útil la siguiente función:

function typeOf(obj) {
  return {}.toString.call(obj).split(' ')[1].slice(0, -1).toLowerCase();
}

O en ES7 (comenta si hay más mejoras)

function typeOf(obj) {
  const { toString } = Object.prototype;
  const stringified = obj::toString();
  const type = stringified.split(' ')[1].slice(0, -1);

  return type.toLowerCase();
}

Resultados:

typeOf(); //undefined
typeOf(null); //null
typeOf(NaN); //number
typeOf(5); //number
typeOf({}); //object
typeOf([]); //array
typeOf(''); //string
typeOf(function () {}); //function
typeOf(/a/) //regexp
typeOf(new Date()) //date
typeOf(new Error) //error
typeOf(Promise.resolve()) //promise
typeOf(function *() {}) //generatorfunction
typeOf(new WeakMap()) //weakmap
typeOf(new Map()) //map

Gracias @johnrees por notificarme de: error, promesa, función del generador

Vix
fuente
Gracias por esto. También funciona para objetos de fecha:typeOf(new Date())
James Irwin
Ah, me había olvidado de eso, gracias, ahora está incluido.
Vix
1
YtypeOf(Promise.resolve())//promise typeOf(function *() {})//generatorfunction
johnrees
No puedo hacer que esta respuesta funcione en mecanografiado. Da error:[ts] Cannot redeclare block-scoped variable 'toString'
DauleDK
No estoy seguro acerca de TypeScript. ¿Has probado la versión ES7? toString no se declara explícitamente podría ser la causa?
Vix
15

También podemos cambiar un pequeño ejemplo de ipr101

Object.prototype.toType = function() {
  return ({}).toString.call(this).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}

y llama como

"aaa".toType(); // 'string'
Greymaster
fuente
66
Recuerde a los niños, manipular objetos base de JavaScript puede generar compatibilidad y otros problemas extraños. ¡Intenta usar esto con moderación tanto en bibliotecas como en proyectos más grandes!
Alexander Craggs
9

función de una línea:

function type(obj) {
    return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/,"$1").toLowerCase()
}

esto da el mismo resultado que jQuery.type()

Yukulélé
fuente
3

Se puede aplicar Object.prototype.toStringa cualquier objeto:

var toString = Object.prototype.toString;

console.log(toString.call([]));
//-> [object Array]

console.log(toString.call(/reg/g));
//-> [object RegExp]

console.log(toString.call({}));
//-> [object Object]

Esto funciona bien en todos los navegadores, con la excepción de IE: al invocar esto en una variable obtenida de otra ventana, simplemente escupirá [object Object].

Andy E
fuente
1
@ Xeon Creo que el odio contra IE es demasiado. IE9 es un navegador mucho mejor que sus predecesores.
Aillyn
44
@pessimopoppotamus pero nadie que actualice su navegador a IE9 usa IE.
Alex Turpin
1
IE 9 es un paso en la dirección correcta, IE 10 se ve bastante bien. Por cierto, el error que mencioné fue una regresión en IE 9 - lo arreglaron en IE 8.
Andy E
3

¡Mis 2 ¢! Realmente, parte de la razón por la que estoy lanzando esto aquí, a pesar de la larga lista de respuestas, es proporcionar una all in onesolución de tipo un poco más y obtener información en el futuro sobre cómo expandirla para incluir más real types.

Con la siguiente solución, como se mencionó anteriormente, combiné un par de soluciones encontradas aquí, así como también incorporé una solución para devolver un valor de jQueryun objeto definido en jQuery si está disponible . También agrego el método al prototipo de objeto nativo. Sé que a menudo es tabú, ya que podría interferir con otras extensiones, pero lo dejo user beware. Si no le gusta esta forma de hacerlo, simplemente copie la función base en cualquier lugar que desee y reemplace todas las variables thiscon un parámetro de argumento para pasar (como los argumentos [0]).

;(function() {  //  Object.realType
    function realType(toLower) {
        var r = typeof this;
        try {
            if (window.hasOwnProperty('jQuery') && this.constructor && this.constructor == jQuery) r = 'jQuery';
            else r = this.constructor && this.constructor.name ? this.constructor.name : Object.prototype.toString.call(this).slice(8, -1);
        }
        catch(e) { if (this['toString']) r = this.toString().slice(8, -1); }
        return !toLower ? r : r.toLowerCase();
    }
    Object['defineProperty'] && !Object.prototype.hasOwnProperty('realType')
        ? Object.defineProperty(Object.prototype, 'realType', { value: realType }) : Object.prototype['realType'] = realType;
})();

Luego, simplemente use con facilidad, así:

obj.realType()  //  would return 'Object'
obj.realType(true)  //  would return 'object'

Nota: Hay 1 argumento aceptable. Si es bool de true, entonces el retorno siempre estará en minúsculas .

Más ejemplos:

true.realType();                            //  "Boolean"
var a = 4; a.realType();                    //  "Number"
$('div:first').realType();                   // "jQuery"
document.createElement('div').realType()    //  "HTMLDivElement"

Si tiene algo que agregar que pueda ser útil, como definir cuándo se creó un objeto con otra biblioteca (Moo, Proto, Yui, Dojo, etc.), siéntase libre de comentar o editar esto y mantenerlo como más exacto y preciso. O pasar al GitHubque hice para hacerme saber. También encontrará un enlace rápido a un archivo cdn min allí.

SpYk3HH
fuente
2
function getType(obj) {
    if(obj && obj.constructor && obj.constructor.name) {
        return obj.constructor.name;
    }
    return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}

En mis pruebas preliminares, esto funciona bastante bien. El primer caso imprimirá el nombre de cualquier objeto creado con "nuevo", y el segundo caso debería capturar todo lo demás.

Estoy usando (8, -1)porque supongo que el resultado siempre comenzará [objecty terminará, ]pero no estoy seguro de que eso sea cierto en cada escenario.

mpen
fuente
2

Supongo que la solución más universal aquí es verificar undefinedy nullprimero, luego simplemente llamar constructor.name.toLowerCase().

const getType = v =>
  v === undefined
    ? 'undefined'
    : v === null
      ? 'null'
      : v.constructor.name.toLowerCase();




console.log(getType(undefined)); // 'undefined'
console.log(getType(null)); // 'null'
console.log(getType('')); // 'string'
console.log(getType([])); // 'array'
console.log(getType({})); // 'object'
console.log(getType(new Set())); // `set'
console.log(getType(Promise.resolve())); // `promise'
console.log(getType(new Map())); // `map'
Arsenowitch
fuente
Hasta donde sé, el .constructor.namesiempre contiene el valor de la cadena. Para undefined / null tenemos las cadenas apropiadas devueltas por la función.
Arsenowitch
Gracias - borrando mis comentarios ...
Bergi
Como constructores una propiedad heredada, esto se rompe para los objetos creados con Object.create(null). Lo suficientemente simple como para arreglarlo, pero ¿cómo llamarlo? "[objeto nulo]"?
traktor53
0

typeof la condición se usa para verificar el tipo de variable, si usted verifica el tipo de variable en la condición if-else

if(typeof Varaible_Name "undefined")
{

}
MzkZeeshan
fuente