Une meilleure façon d'obtenir le type d'une variable Javascript?

260

Existe-t-il un meilleur moyen d'obtenir le type d'une variable dans JS que typeof? Cela fonctionne bien lorsque vous faites:

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

Mais c'est inutile quand vous essayez:

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

Je connais instanceof, mais cela vous oblige à connaître le type au préalable.

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

Y a-t-il une meilleure façon?

Aillyn
la source
1
Pour info, le typeof new RegExp(/./); // "function"problème dans Chrome semble être résolu dans Chrome 14.
user113716
Avertissement: si vous réduisez votre JS et que vous utilisez des `` objets personnalisés '' tels que des classes dactylographiées, certaines des réponses ci-dessous finiront par vous donner le nom de la fonction obscurcie, par exemple, au nlieu du nom d'origine attendu. par exemple, constructor.namepourrait vous donner nau lieu du nom complet attendu
Simon_Weaver

Réponses:

222

Angus Croll a récemment écrit un article de blog intéressant à ce sujet -

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

Il passe par le pour et le contre des différentes méthodes puis définit une nouvelle méthode 'toType' -

var toType = function(obj) {
  return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
ipr101
la source
3
Remarque intéressante ici - cela pourrait potentiellement casser où un objet "hôte" est passé à la fonction. Les ActiveXObjectinstances d' Internet Explorer , par exemple.
Andy E,
Si vous avez créé votre propre type d'objet (héritage prototypique), comment pourriez-vous obtenir qu'il retourne une valeur plus spécifique que "objet"? Cette question a également été soulevée ici , mais n'a jamais été abordée.
EleventyOne
1
Si la dénomination de votre type contient: ou _ vous pouvez étendre cette expression régulière àmatch(/\s([a-z,_,:,A-Z]+)/)
masi
6
Le regex devrait également inclure des nombres pour des choses comme Uint8Array. Au lieu de cela, considérez le plus général match(/\s([^\]]+)/)qui devrait gérer n'importe quoi.
Lily Finley
2
Je suggère d'utiliser le \wmot opérateur à la place…
kaiser
51

Vous pouvez essayer d'utiliser constructor.name.

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

Comme avec tout JavaScript, quelqu'un finira par indiquer invariablement que c'est en quelque sorte mauvais, alors voici un lien vers une réponse qui couvre assez bien cela.

Une alternative est d'utiliser Object.prototype.toString.call

Object.prototype.toString.call([])
Object.prototype.toString.call(/./)
Alex Turpin
la source
10
nameest une extension d'ECMAScript et ne fonctionnera pas dans tous les navigateurs.
Andy E
16
Réponse votée en raison de la confirmation de soupçons selon lesquels tout ce qui est fait en javascript est en quelque sorte mauvais.
liljoshu
1
MAUVAIS: si vous réduisez votre code, cela constructor.namepeut probablement ressembler à la nplace du nom d'objet que vous attendez. Alors faites attention à cela - surtout si vous ne faites que minifier en production.
Simon_Weaver
nullet undefinedmalheureusement, je n'ai pas de constructeur.
Andrew Grimm
JavaScript lui-même est mauvais. Et idiot. Le fait que cette question n'ait pas de bonne réponse en est la preuve vivante.
user2173353
38

Une fonction de capture de type raisonnablement bonne est celle utilisée par 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');
};

Cela capture de nombreuses primitives fournies par javascript, mais vous pouvez toujours en ajouter d'autres en modifiant l' TYPESobjet. Notez que typeof HTMLElementCollectiondans Safari signalera function, mais tapez (HTMLElementCollection) retourneraobject

Nick Husher
la source
31

Vous pouvez trouver la fonction suivante utile:

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

Ou dans ES7 (commentez si d'autres améliorations)

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

  return type.toLowerCase();
}

Résultats:

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

Merci @johnrees de m'avoir informé de: erreur, promesse, fonction de générateur

Vix
la source
Merci pour cela. Il fonctionne également pour les objets de date:typeOf(new Date())
James Irwin
Ah, j'avais oublié ça - merci, c'est maintenant inclus.
Vix
1
EttypeOf(Promise.resolve())//promise typeOf(function *() {})//generatorfunction
johnrees
Je n'arrive pas à faire fonctionner cette réponse en dactylographie. Donne une erreur:[ts] Cannot redeclare block-scoped variable 'toString'
DauleDK
Pas sûr de TypeScript. Avez-vous essayé la version ES7? toString n'étant pas explicitement déclaré pourrait être la cause?
Vix
15

Nous pouvons également changer un petit exemple de ipr101

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

et appeler comme

"aaa".toType(); // 'string'
Greymaster
la source
6
N'oubliez pas les enfants, la manipulation d'objets de base JavaScript peut entraîner une compatibilité et d'autres problèmes étranges. Essayez de l'utiliser avec parcimonie dans les bibliothèques et les grands projets!
Alexander Craggs
9

une fonction de ligne:

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

cela donne le même résultat que jQuery.type()

Yukulélé
la source
3

Vous pouvez appliquer Object.prototype.toStringà n'importe quel objet:

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]

Cela fonctionne bien dans tous les navigateurs, à l'exception d'IE - lorsque vous appelez cela sur une variable obtenue à partir d'une autre fenêtre, cela crache [object Object].

Andy E
la source
1
@Xeon Je pense que la haine contre IE est un peu trop. IE9 est un navigateur bien meilleur que ses prédécesseurs.
Aillyn
4
@pessimopoppotamus mais personne qui voudrait mettre à jour son navigateur vers IE9 n'utilise IE.
Alex Turpin
1
IE 9 est un pas dans la bonne direction, IE 10 semble plutôt bien. Par ailleurs, le bug que j'ai mentionné était une régression dans IE 9 - ils l'ont corrigé dans IE 8.
Andy E
3

Mon 2 ¢! Vraiment, une partie de la raison pour laquelle je lance ceci ici, malgré la longue liste de réponses, est de fournir un peu plusall in one solution de type et d'obtenir des informations à l'avenir sur la façon de l'étendre pour en inclure davantage real types.

Avec la solution suivante, comme mentionné ci-dessus, j'ai combiné quelques solutions trouvées ici, ainsi que l'incorporation d'un correctif pour renvoyer une valeur de l' jQueryobjet défini sur jQuery si disponible . J'ajoute également la méthode au prototype d'objet natif. Je sais que c'est souvent tabou, car cela pourrait interférer avec d'autres extensions de ce type, mais je laisse cela àuser beware . Si vous n'aimez pas cette façon de faire, copiez simplement la fonction de base où vous voulez et remplacez toutes les variables de thispar un paramètre d'argument à passer (comme les arguments [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;
})();

Ensuite, utilisez simplement avec facilité, comme ceci:

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

Remarque: Il y a 1 argument passable. Si est bool detrue , le retour sera toujours en minuscules .

Plus d'exemples:

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

Si vous avez quelque chose à ajouter qui peut être utile, comme définir quand un objet a été créé avec une autre bibliothèque (Moo, Proto, Yui, Dojo, etc ...), n'hésitez pas à commenter ou à modifier cela et à le garder plus précis et précis. OU passez à la GitHubje l' ai fait et faites le moi savoir. Vous y trouverez également un lien rapide vers un fichier cdn min.

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

Dans mes tests préliminaires, cela fonctionne plutôt bien. Le premier cas affichera le nom de tout objet créé avec "nouveau", et le deuxième cas devrait attraper tout le reste.

J'utilise (8, -1)parce que je suppose que le résultat va toujours commencer [objectet se terminer, ]mais je ne suis pas certain que ce soit vrai dans tous les scénarios.

mpen
la source
2

Je suppose que la solution la plus universelle ici - est de vérifier undefinedet d' nullabord, puis d'appeler 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
la source
Pour autant que je sache - le .constructor.namecontient toujours la valeur de la chaîne. Pour undefined / null, nous avons les chaînes appropriées retournées par la fonction.
Arsenowitch
Merci - en supprimant mes commentaires…
Bergi
Étant donné qu'il constructors'agit d'une propriété héritée, cela interrompt les objets créés avec Object.create(null). Assez simple à réparer, mais comment l'appeler? "[objet nul]"?
traktor53
0

typeof condition est utilisée pour vérifier le type de variable, si vous contrôlez le type de variable dans la condition if-else, par exemple

if(typeof Varaible_Name "undefined")
{

}
MzkZeeshan
la source