Melhor maneira de obter o tipo de uma variável Javascript?

260

Existe uma maneira melhor de obter o tipo de uma variável em JS do que typeof? Funciona bem quando você faz:

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

Mas é inútil quando você tenta:

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

Eu sei instanceof, mas isso exige que você saiba o tipo de antemão.

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

Existe uma maneira melhor?

Aillyn
fonte
1
FYI, o typeof new RegExp(/./); // "function"problema no Chrome parece ser fixado no Chrome 14.
user113716
2
possível duplicata de Como obtenho o nome do tipo de um objeto em JavaScript?
Aillyn 12/09/11
Aviso: se você estiver minificando seu JS e usando 'objetos personalizados', como classes de texto datilografado, algumas das respostas abaixo acabarão fornecendo o nome da função ofuscada, como em nvez do nome original esperado. por exemplo, constructor.namepode dar-lhe n, em vez do nome completo esperado
Simon_Weaver

Respostas:

221

Angus Croll escreveu recentemente um post interessante no blog sobre isso -

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

Ele analisa os prós e contras dos vários métodos e define um novo método 'toType' -

var toType = function(obj) {
  return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
ipr101
fonte
3
Nota lateral interessante aqui - isso pode ser interrompido onde um objeto "host" é passado para a função. ActiveXObjectInstâncias do Internet Explorer , por exemplo.
Andy E
Se você criou seu próprio tipo de objeto (herança prototípica), como conseguir que ele retorne um valor mais específico que "objeto"? Isso também foi levantado como um problema aqui , mas nunca foi abordado.
EleventyOne 27/07
1
Se o seu tipo de nomeação contém: ou _ você pode estender esse regex paramatch(/\s([a-z,_,:,A-Z]+)/)
masi
6
O regex também precisaria incluir números para coisas como Uint8Array. Em vez disso, considere o mais geral match(/\s([^\]]+)/)que deve lidar com qualquer coisa.
Lily Finley
2
Eu sugiro usar o \woperador palavra em vez ...
kaiser
51

Você pode tentar usar constructor.name.

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

Como em todo JavaScript, alguém eventualmente invariavelmente aponta que isso é de alguma forma ruim, então aqui está um link para uma resposta que cobre isso muito bem.

Uma alternativa é usar Object.prototype.toString.call

Object.prototype.toString.call([])
Object.prototype.toString.call(/./)
Alex Turpin
fonte
10
nameé uma extensão do ECMAScript e não funcionará em todos os navegadores.
Andy E
16
Resposta votada devido à confirmação de suspeita de que tudo o que é feito em javascript é de alguma forma ruim.
liljoshu
1
MAL: se você está minificando seu código, constructor.nameprovavelmente pode acabar sendo algo como, em nvez do nome do objeto que você espera. Portanto, tenha cuidado com isso - especialmente se você reduzir apenas a produção.
Simon_Weaver
nulle undefinedinfelizmente não tem um construtor.
Andrew Grimm
O próprio JavaScript é mau. E bobo. O fato de esta pergunta não ter uma boa resposta é a prova viva disso.
user2173353
38

Uma função de captura de tipo razoavelmente boa é aquela usada pelo 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');
};

Isso captura muitas das primitivas fornecidas pelo javascript, mas você sempre pode adicionar mais modificando o TYPESobjeto. Observe que typeof HTMLElementCollectionno Safari será relatado function, mas o tipo (HTMLElementCollection) retornaráobject

Nick Husher
fonte
31

Você pode achar útil a seguinte função:

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

Ou no ES7 (comente se houver mais melhorias)

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

Obrigado @johnrees por me notificar de: error, promessa, generatorfunction

Vix
fonte
Obrigado por isso. Também funciona para objetos de data:typeOf(new Date())
James Irwin
Ah, eu tinha esquecido disso - obrigado, agora está incluído.
Vix
1
EtypeOf(Promise.resolve())//promise typeOf(function *() {})//generatorfunction
johnrees 30/09
Não consigo obter esta resposta para trabalhar em texto datilografado. Dá erro:[ts] Cannot redeclare block-scoped variable 'toString'
DauleDK
Não tenho certeza sobre o TypeScript. Você já experimentou a versão ES7? toString não declarado explicitamente pode ser a causa?
Vix 25/10
15

Também podemos mudar um pequeno exemplo de ipr101

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

e chame como

"aaa".toType(); // 'string'
greymaster
fonte
6
Lembre-se de crianças, manipular objetos básicos do JavaScript pode levar à compatibilidade e a outros problemas estranhos. Tente usar isso moderadamente em bibliotecas e em projetos maiores!
Alexander Craggs
9

função de uma linha:

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

isso dá o mesmo resultado que jQuery.type()

Yukulélé
fonte
3

Você pode aplicar Object.prototype.toStringa qualquer 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]

Isso funciona bem em todos os navegadores, com exceção do IE - ao chamar isso em uma variável obtida de outra janela, ela é expelida [object Object].

Andy E
fonte
1
@ Xeon Eu acho que o ódio contra o IE é um pouco demais. O IE9 é um navegador muito melhor que seus antecessores.
Aillyn
4
@pessimopoppotamus, mas ninguém que atualizaria seu navegador para o IE9 usa o IE.
Alex Turpin
1
O IE 9 é um passo na direção certa, o IE 10 está muito bom. Incidentalmente, o erro I mencionados era uma regressão no IE 9 - que fixas no IE 8.
Andy E
3

Meus 2 ¢! Realmente, parte da razão pela qual estou lançando isso aqui, apesar da longa lista de respostas, é fornecer um pouco mais de all in onesolução de tipo e receber um feedback no futuro sobre como expandi-lo para incluir mais real types.

Com a solução a seguir, como mencionado acima, eu combinei algumas soluções encontradas aqui, além de incorporar uma correção para retornar um valor jQueryno objeto definido no jQuery, se disponível . Também anexo o método ao protótipo de objeto nativo. Eu sei que isso geralmente é um tabu, pois pode interferir em outras extensões, mas deixo isso para user beware. Se você não gosta dessa maneira, simplesmente copie a função base em qualquer lugar que desejar e substitua todas as variáveis thispor um parâmetro de argumento para passar (como 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;
})();

Em seguida, basta usar com facilidade, assim:

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

Nota: Há 1 argumento aceitável. Se for bool de true, o retorno estará sempre em minúsculas .

Mais exemplos:

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

Se você tiver algo a acrescentar que talvez seja útil, como definir quando um objeto foi criado com outra biblioteca (Moo, Proto, Yui, Dojo, etc ...), sinta-se à vontade para comentar ou editar isso e mantê-lo mais preciso e preciso. OU passe para o que GitHubeu fiz e me avise. Você também encontrará um link rápido para um arquivo cdn min.

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

Nos meus testes preliminares, isso está funcionando muito bem. O primeiro caso imprimirá o nome de qualquer objeto criado com "novo", e o segundo caso deverá capturar todo o resto.

Estou usando (8, -1)porque estou assumindo que o resultado sempre começará [objecte terminará, ]mas não tenho certeza de que seja verdade em todos os cenários.

mpen
fonte
2

Eu acho que a solução mais universal aqui - é para verificar se undefinede nullem primeiro lugar, em seguida, é só chamar 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
fonte
Até onde eu sei - o .constructor.namesempre contém o valor da string. Para indefinido / nulo, temos as seqüências apropriadas retornadas pela função.
Arsenowitch # 25/19
Obrigado - excluindo meus comentários ...
Bergi
Como constructoré uma propriedade herdada, isso interrompe os objetos criados com Object.create(null). Simples o suficiente para consertar, mas como chamá-lo? "[objeto nulo]"?
traktor53
0

typeof condição é usada para verificar o tipo de variável, se você estiver verificar o tipo de variável na condição if-else, por exemplo

if(typeof Varaible_Name "undefined")
{

}
MzkZeeshan
fonte