Il modo più preciso per verificare il tipo di oggetto JS?


138

Il typeof operatore non ci aiuta davvero a trovare il tipo reale di un oggetto.

Ho già visto il seguente codice:

Object.prototype.toString.apply(t)  

Domanda:

È il modo più accurato di verificare il tipo di oggetto?


2

4
Un'occhiata a questo post: stackoverflow.com/questions/332422/...
isJustMe


3
Il modo più accurato è ... non testare il tipo. Perché hai bisogno dei tipi?
hugomg

Object.prototype.toString.call / Object.prototype.toString.apply
xgqfrms

Risposte:


191

La specifica JavaScript fornisce esattamente un modo corretto per determinare la classe di un oggetto:

Object.prototype.toString.call(t);

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


5
Se stai cercando un tipo specifico, probabilmente vorrai fare qualcosa del genere: il Object.prototype.toString.call(new FormData()) === "[object FormData]"che sarebbe vero. È inoltre possibile utilizzare slice(8, -1)per restituire FormDatainvece di[object FormData]
Chris Marisic il

4
C'è qualche differenza tra usare Object.prototypee {}?
GetFree,

3
forse questo è cambiato nel corso degli anni, ma Object.prototype.toString.call(new MyCustomObject())ritorna [object Object]mentre new MyCustomObject() instanceOf MyCustomObject returns trueè quello che volevo (Chrome 54.0.2840.99 m)
Maslow

@Maslow, ho riscontrato lo stesso problema che hai sollevato. Dopo aver consultato la documentazione online, ho finito per usare new MyCustomObject().constructor === MyCustomObject.
solstizio333,

3
Sorge la domanda, perché non hanno racchiuso questo codice in un metodo più conveniente o hanno permesso a un operatore aggiuntivo di compilare questo. So che sei solo il messaggero, ma francamente è terribile.
Andrew S

60

il Object.prototype.toStringè un buon modo, ma le prestazioni è il peggiore.

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

controlla le prestazioni del tipo js

Utilizzare typeofper risolvere alcuni problemi di base (String, Number, Boolean ...) e utilizzare Object.prototype.toStringper risolvere qualcosa di complesso (come Array, Date, RegExp).

e questa è la mia soluzione:

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

usare come:

type(function(){}); // -> "function"
type([1, 2, 3]); // -> "array"
type(new Date()); // -> "date"
type({}); // -> "object"

Quel test su jsPerf non è abbastanza preciso. Quei test non sono uguali (test per la stessa cosa). Ad esempio, typeof [] restituisce "object", anche typeof {} restituisce "object", anche se uno è un array di oggetti e l'altro è un oggetto Object. Ci sono molti altri problemi con quel test ... Fai attenzione quando guardi jsPerf che i test stanno confrontando le Mele alle Mele.
kmatheny,

La tua typefunzione è buona, ma guarda come funziona rispetto ad alcune altre typefunzioni. http://jsperf.com/code-type-test-a-test
Progo

18
Queste metriche delle prestazioni dovrebbero essere temperate con un certo senso comune. Certo, prototype.toString è più lento degli altri di un ordine di grandezza, ma nel grande schema delle cose richiede in media un paio di centinaia di nanosecondi per chiamata. A meno che questa chiamata non venga utilizzata in un percorso critico eseguito molto frequentemente, ciò è probabilmente innocuo. Preferirei avere un codice diretto piuttosto che un codice che termina un microsecondo più velocemente.
David,

({}).toString.call(obj)è più lento di Object.prototype.toString jsperf.com/object-check-test77
timaschew

Bella soluzione. Prendo in prestito la tua funzione nella mia lib :)
Dong Nguyen

19

La risposta accettata è corretta, ma mi piace definire questa piccola utility nella maggior parte dei progetti che costruisco.

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]',
}

Usato così:

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

}

Se stai usando l'angolazione puoi anche farla iniettare in modo pulito:

angular.constant('types', types);

11
var o = ...
var proto =  Object.getPrototypeOf(o);
proto === SomeThing;

Tieni sotto controllo il prototipo che prevedi abbia l'oggetto, quindi confronta con esso.

per esempio

var o = "someString";
var proto =  Object.getPrototypeOf(o);
proto === String.prototype; // true

In che modo è meglio / diverso dal dire o instanceof String; //true?
Jamie Treworgy,

@jamietre because "foo" instanceof Stringbreak
Raynos,

OK, quindi "typeof (o) === 'oggetto' && o instanceof SomeObject". È facile testare le stringhe. Sembra solo un lavoro extra, senza risolvere il problema di base di dover sapere in anticipo per cosa stai testando.
Jamie Treworgy,

Mi dispiace che lo snippet di codice non abbia senso, ma penso che tu sappia cosa intendo, se stai testando le stringhe, quindi usa typeof(x)==='string'invece.
Jamie Treworgy,

A proposito, Object.getPrototypeOf(true)non riesce dove (true).constructorritorna Boolean.
Katspaugh,

5

Direi che la maggior parte delle soluzioni mostrate qui soffrono di un eccesso di ingegnere. Probabilmente il modo più semplice per verificare se un valore è di tipo [object Object]è verificare con la .constructorproprietà di esso:

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

o anche più breve con le funzioni freccia:

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

La a != nullparte è necessaria perché si potrebbe passare nulloundefined e non è possibile estrarre una proprietà del costruttore da una di queste.

Funziona con qualsiasi oggetto creato tramite:

  • il Objectcostruttore
  • letterali {}

Un'altra caratteristica interessante è la sua capacità di fornire report corretti per le classi personalizzate che ne fanno uso Symbol.toStringTag. Per esempio:

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

Il problema qui è che quando si chiama Object.prototype.toStringun'istanza di esso, [object Object]verrà restituito il rapporto falso :

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

Ma il controllo contro il costruttore dà un risultato corretto:

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

4

Il modo migliore per scoprire il tipo REAL di un oggetto (incluso ENTRAMBI il nome Object o DataType nativo (come String, Date, Number, ..etc) E il tipo REAL di un oggetto (anche quelli personalizzati); è afferrando la proprietà name del costruttore del prototipo dell'oggetto:

Tipo nativo Ex1:

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

display:

String

Ex2:

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

display:

Array

Classi personalizzate:

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

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

display:

CustomClass

Ciò non riesce se l'oggetto è nullo undefined.
Julian Knight,

2

Vecchia domanda che conosco. Non è necessario convertirlo. Vedi questa funzione:

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;

}; 

Esempi:

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

Basso costo e semplice.


Restituisce falsese l'oggetto del test è nulloundefined
Julian Knight il

oppure trueoppurefalse
Julian Knight il

@JulianKnight false va bene a null o indefinito, non è niente di utile. Quindi qual è il punto?
Codebeat

il tuo esempio restituisce dati incoerenti. Alcuni risultati sono il tipo di dati e altri il valore false. In che modo questo aiuta a rispondere alla domanda?
Julian Knight,

1
@JulianKnight Vedi le modifiche, è quello che vuoi? Se si preferisce undefined o "undefined" come risultato, è possibile sostituire l'ultimo falso, se lo si desidera.
Codebeat

0

Ho messo insieme una piccola utility di controllo del tipo ispirata alle risposte corrette sopra:

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

esempio:

if (thetypeof(prop).isnt('String')) thetypeof(prop).error('String')
if (thetypeof(prop).is('Number')) // do something

Non sembra funzionare con oggetti che sono nullo undefinedo trueofalse
Julian Knight
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.