A maneira mais precisa de verificar o tipo de objeto JS?

137

O typeofoperador realmente não nos ajuda a encontrar o tipo real de um objeto.

Eu já vi o seguinte código:

Object.prototype.toString.apply(t)  

Questão:

É a maneira mais precisa de verificar o tipo do objeto?

Royi Namir
fonte
2
Dê uma olhada neste artigo: javascriptweblog.wordpress.com/2011/08/08/…
James Allardice
4
Veja esta postagem: stackoverflow.com/questions/332422/…
isJustMe
3
A maneira mais precisa é ... não testar o tipo. Por que você precisa dos tipos?
hugomg 25/10
Object.prototype.toString.call / Object.prototype.toString.apply
xgqfrms

Respostas:

190

A especificação JavaScript fornece exatamente uma maneira adequada de determinar a classe de um objeto:

Object.prototype.toString.call(t);

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

kmatheny
fonte
5
Se você estiver procurando por um tipo específico, provavelmente desejaria fazer algo ao longo das linhas de: o Object.prototype.toString.call(new FormData()) === "[object FormData]"que seria verdade. Você também pode usar slice(8, -1)para retornar em FormDatavez de[object FormData]
Chris Marisic 17/14/14
4
Existe alguma diferença entre usar Object.prototypee {}?
GetFree,
3
talvez isso mudou ao longo dos anos, mas Object.prototype.toString.call(new MyCustomObject())retorna [object Object]ao passo new MyCustomObject() instanceOf MyCustomObject returns trueque é o que eu queria (Chrome 54.0.2840.99 m)
Maslow
@ Maslow, eu corri para o mesmo problema que você levantou. Depois de examinar algumas documentações online, acabei usando new MyCustomObject().constructor === MyCustomObject.
solstice333
3
Isso levanta a questão: por que eles não envolveram esse código em um método mais conveniente ou permitiram que um operador adicional compilasse isso? Eu sei que você é apenas o mensageiro, mas, francamente, isso é horrível.
Andrew
60

o Object.prototype.toStringé uma boa maneira, mas seu desempenho é o pior.

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

verifique o desempenho do tipo js

Use typeofpara resolver algum problema básico (String, Number, Boolean ...) e use Object.prototype.toStringpara resolver algo complexo (como Array, Date, RegExp).

e esta é a minha solução:

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

use como:

type(function(){}); // -> "function"
type([1, 2, 3]); // -> "array"
type(new Date()); // -> "date"
type({}); // -> "object"
wiky
fonte
Esse teste no jsPerf não é muito preciso. Esses testes não são iguais (teste para a mesma coisa). Por exemplo, typeof [] retorna "objeto", typeof {} também retorna "objeto", mesmo que um seja um objeto Array e o outro seja um objeto Object. Existem muitos outros problemas com esse teste ... Cuidado ao olhar para o jsPerf que os testes estão comparando maçãs com maçãs.
kmatheny
Sua typefunção é boa, mas veja como ela se compara a outras typefunções. http://jsperf.com/code-type-test-a-test
Progo
18
Essas métricas de desempenho devem ser temperadas com algum senso comum. Certamente, o protótipo.toString é mais lento que os outros em uma ordem de grandeza, mas no grande esquema de coisas leva em média algumas centenas de nanossegundos por chamada. A menos que essa chamada esteja sendo usada em um caminho crítico executado com muita frequência, isso provavelmente é inofensivo. Prefiro ter código direto do que código que termina um microssegundo mais rápido.
David
({}).toString.call(obj)é mais lento do que Object.prototype.toString jsperf.com/object-check-test77
timaschew
Ótima solução. Eu pedir sua função no meu lib :)
Dong Nguyen
19

A resposta aceita está correta, mas eu gosto de definir esse pequeno utilitário na maioria dos projetos que construo.

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 assim:

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

}

Se você estiver usando angular, você pode até injetá-lo de forma limpa:

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

Mantenha um controle sobre o protótipo que você espera que o objeto tenha e depois compare-o.

por exemplo

var o = "someString";
var proto =  Object.getPrototypeOf(o);
proto === String.prototype; // true
Raynos
fonte
Como isso é melhor / diferente do que dizer o instanceof String; //true?
Jamie Treworgy
@jamietre porque "foo" instanceof Stringquebra
Raynos
OK, então "typeof (o) === 'objeto' && o instanceof SomeObject". É fácil testar se há strings. Parece um trabalho extra, sem resolver o problema básico de ter que saber com antecedência o que você está testando.
Jamie Treworgy
Desculpe que o snippet de código não faz sentido, mas acho que você entende o que quero dizer, se estiver testando cadeias de caracteres, use-o typeof(x)==='string'.
Jamie Treworgy
BTW, Object.getPrototypeOf(true)falha onde (true).constructorretorna Boolean.
katspaugh
5

Eu diria que a maioria das soluções mostradas aqui sofre de excesso de engenharia. Provavelmente, a maneira mais simples de verificar se um valor é do tipo [object Object]é verificar a .constructorpropriedade dele:

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

ou ainda mais curto com as funções de seta:

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

A a != nullpeça é necessária porque é possível passar nullou undefinedvocê não pode extrair uma propriedade de construtor de nenhuma delas.

Funciona com qualquer objeto criado via:

  • o Objectconstrutor
  • literais {}

Outro recurso interessante é a capacidade de fornecer relatórios corretos para classes personalizadas que são utilizadas Symbol.toStringTag. Por exemplo:

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

O problema aqui é que, ao chamar Object.prototype.toStringuma instância dela, o relatório falso [object Object]será retornado:

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

Mas a verificação no construtor fornece um resultado correto:

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

A melhor maneira de descobrir o tipo REAL de um objeto (incluindo AMBOS o nome nativo do objeto ou DataType (como String, Data, Número, etc. etc) E o tipo REAL de um objeto (mesmo os personalizados); a propriedade name do construtor do protótipo do objeto:

Tipo nativo Ex1:

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

exibe:

String

Ex2:

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

exibe:

Array

Classes personalizadas:

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

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

exibe:

CustomClass
beliha
fonte
Isso falhará se o objeto for nullou undefined.
Julian Knight
2

Velha pergunta eu sei. Você não precisa convertê-lo. Veja esta função:

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;

}; 

Exemplos:

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

Baixo custo e simples.

Codebeat
fonte
Retorna falsese o objeto de teste é nullouundefined
Julian Knight
ou trueoufalse
Julian Knight
@JulianKnight false não tem problema em nulo ou indefinido, não é nada útil. Então qual é o ponto?
Codebeat
seu exemplo retorna dados inconsistentes. Alguns resultados são do tipo de dados e outros são do valor false. Como isso ajuda a responder à pergunta?
Julian Knight
1
@JulianKnight Veja as mudanças, é isso que você quer? Se você preferir indefinido ou "indefinido" como resultado, poderá substituir o último falso, se desejar.
Codebeat 16/06
0

Eu montei um pequeno utilitário de verificação de tipo inspirado nas respostas corretas acima:

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

exemplo:

if (thetypeof(prop).isnt('String')) thetypeof(prop).error('String')
if (thetypeof(prop).is('Number')) // do something
Abraham Juliot
fonte
Parece não funcionar para objetos que são nullou undefinedou trueoufalse
Julian Knight