Modo migliore per ottenere il tipo di una variabile Javascript?


260

C'è un modo migliore per ottenere il tipo di una variabile in JS rispetto a typeof? Funziona bene quando lo fai:

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

Ma è inutile quando provi:

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

Lo so instanceof, ma questo richiede che tu sappia il tipo in anticipo.

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

C'è un modo migliore?


1
Cordiali saluti, il typeof new RegExp(/./); // "function"problema in Chrome sembra essere risolto in Chrome 14.
user113716


Avvertenza: se stai minimizzando il tuo JS e stai usando "oggetti personalizzati" come le classi dattiloscritte, alcune delle risposte sottostanti finiranno per darti il ​​nome della funzione offuscata come ninvece del nome originale previsto. ad esempio constructor.namepotrebbe darti ninvece del nome completo previsto
Simon_Weaver

Risposte:


221

Angus Croll ha recentemente scritto un interessante post sul blog su questo -

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

Esamina i pro e i contro dei vari metodi, quindi definisce un nuovo metodo "toType" -

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

3
Nota a margine interessante qui - questo potrebbe potenzialmente rompersi dove un oggetto "host" viene passato alla funzione. ActiveXObjectIstanze di Internet Explorer , ad esempio.
Andy E

Se hai creato il tuo tipo di oggetto (eredità prototipale), come potresti ottenerlo per restituire un valore più specifico di "oggetto"? Anche questo è stato sollevato come un problema qui , ma non è mai stato affrontato.
EleventyOne,

1
Se la tua nomenclatura contiene: o _ puoi estendere questa regex amatch(/\s([a-z,_,:,A-Z]+)/)
masi il

6
La regex dovrebbe anche includere numeri per cose come Uint8Array. Invece, considera il più generale match(/\s([^\]]+)/)che dovrebbe gestire qualsiasi cosa.
Lily Finley,

2
Suggerirei invece di usare il \wword operator ...
kaiser,

51

Puoi provare a usare constructor.name.

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

Come con tutto JavaScript, qualcuno alla fine indicherà sempre che questo è in qualche modo malvagio, quindi ecco un link a una risposta che copre abbastanza bene.

Un'alternativa è usare Object.prototype.toString.call

Object.prototype.toString.call([])
Object.prototype.toString.call(/./)

10
nameè un'estensione di ECMAScript e non funzionerà in tutti i browser.
Andy E

16
Rispondi al voto a causa della conferma del sospetto che tutto ciò che è stato fatto in JavaScript sia in qualche modo malvagio.
liljoshu,

1
MALE: se stai minimizzando il tuo codice, constructor.nameprobabilmente finirà per essere qualcosa di simile ninvece del nome dell'oggetto che ti aspetti. Quindi state attenti, specialmente se minimizzate solo in produzione.
Simon_Weaver

nulle undefinedpurtroppo non hanno un costruttore.
Andrew Grimm,

JavaScript stesso è malvagio. E sciocco. Il fatto che questa domanda non abbia una buona risposta ne è la prova vivente.
user2173353

38

Una funzione di acquisizione del tipo ragionevolmente buona è quella usata da 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');
};

Questo cattura molte delle primitive fornite da JavaScript, ma puoi sempre aggiungerne altre modificando l' TYPESoggetto. Si noti che typeof HTMLElementCollectionin Safari verrà segnalato function, ma verrà restituito il tipo (HTMLElementCollection)object


31

Potresti trovare utile la seguente funzione:

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

O in ES7 (commento se ulteriori miglioramenti)

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

  return type.toLowerCase();
}

risultati:

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

Grazie @johnrees per avermi avvisato di: errore, promessa, funzione generatore


Grazie per questo. Funziona anche con oggetti data:typeOf(new Date())
James Irwin,

Ah, me ne ero dimenticato - grazie, ora è incluso.
Vix,

1
EtypeOf(Promise.resolve())//promise typeOf(function *() {})//generatorfunction
johnrees il

Non riesco a far funzionare questa risposta in dattiloscritto. Fornisce errore:[ts] Cannot redeclare block-scoped variable 'toString'
DauleDK il

Non sono sicuro di TypeScript. Hai provato la versione ES7? toString non essere esplicitamente dichiarato potrebbe essere la causa?
Vix,

15

Inoltre possiamo cambiare un piccolo esempio da ipr101

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

e chiama come

"aaa".toType(); // 'string'

6
Ricorda i bambini, manipolare gli oggetti di base JavaScript può portare alla compatibilità e ad altri strani problemi. Prova a usarlo con parsimonia in entrambe le biblioteche e in progetti più grandi!
Alexander Craggs,

9

funzione a una riga:

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

questo dà lo stesso risultato di jQuery.type()


3

Puoi applicare Object.prototype.toStringa qualsiasi oggetto:

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]

Funziona bene in tutti i browser, ad eccezione di IE: quando si chiama questo su una variabile ottenuta da un'altra finestra verrà semplicemente sputato [object Object].


1
@Xeon Penso che l'odio contro IE sia un po 'troppo. IE9 è un browser molto migliore rispetto ai suoi predecessori.
Aillyn,

4
@pessimopoppotamus ma nessuno che vorrebbe aggiornare il proprio browser a IE9 utilizza IE.
Alex Turpin,

1
IE 9 è un passo nella giusta direzione, IE 10 sembra piuttosto buono. Per inciso, il bug che ho citato era una regressione in IE 9 - l'hanno risolto in IE 8.
Andy E

3

Il mio 2 ¢! In realtà, parte del motivo per cui lo sto lanciando qui, nonostante la lunga lista di risposte, è quello di fornire una all in onesoluzione un po 'più di tipo e ottenere un feedback in futuro su come espanderlo per includerne altri real types.

Con la seguente soluzione, come già detto, ho combinato un paio di soluzioni trovate qui, oltre a incorporare una correzione per restituire un valore jQuerydell'oggetto definito su jQuery se disponibile . Aggiungo anche il metodo al prototipo di oggetto nativo. So che è spesso un tabù, in quanto potrebbe interferire con altre estensioni del genere, ma lo lascio a user beware. Se non ti piace questo modo di farlo, copia semplicemente la funzione base dove preferisci e sostituisci tutte le variabili thiscon un parametro argomento da passare (come argomenti [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;
})();

Quindi utilizzare semplicemente con facilità, in questo modo:

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

Nota: esiste 1 argomento accettabile. Se è bool di true, allora il ritorno sarà sempre in minuscolo .

Altri esempi:

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

Se hai qualcosa da aggiungere che potrebbe essere utile, come definire quando un oggetto è stato creato con un'altra libreria (Moo, Proto, Yui, Dojo, ecc ...), non esitare a commentare o modificare questo e continuare ad essere più accurato e preciso. OPPURE passare a quello che GitHubho creato e farmelo sapere. Qui troverai anche un collegamento rapido a un file cdn min.


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

Nei miei test preliminari, questo funziona abbastanza bene. Il primo caso stamperà il nome di qualsiasi oggetto creato con "nuovo" e il secondo caso dovrebbe catturare tutto il resto.

Lo sto usando (8, -1)perché presumo che il risultato inizierà [objecte finirà sempre, ]ma non sono sicuro che sia vero in ogni scenario.


2

Immagino che la soluzione più universale qui sia quella di verificare undefinede nullprima di tutto, quindi chiamare 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'

Per quanto ne so, .constructor.namecontiene sempre il valore della stringa. Per undefined / null abbiamo le stringhe appropriate restituite dalla funzione.
Arsenowitch,

Grazie - eliminando i miei commenti ...
Bergi il

Poiché constructorè una proprietà ereditata, questa si interrompe per gli oggetti creati con Object.create(null). Abbastanza semplice da risolvere, ma come chiamarlo? "[oggetto null]"?
traktor53,

0

typeof condition viene utilizzata per controllare il tipo di variabile, se si controlla il tipo di variabile in if-else condition ad es

if(typeof Varaible_Name "undefined")
{

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