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.