Le moyen le plus précis de vérifier le type d'objet JS?


138

L' typeofopérateur ne nous aide pas vraiment à trouver le type réel d'un objet.

J'ai déjà vu le code suivant:

Object.prototype.toString.apply(t)  

Question:

Est-ce le moyen le plus précis de vérifier le type de l'objet?





3
Le moyen le plus précis est ... de ne pas tester le type. Pourquoi avez-vous besoin des types?
hugomg

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

Réponses:


191

La spécification JavaScript donne exactement une façon appropriée de déterminer la classe d'un objet:

Object.prototype.toString.call(t);

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


5
Si vous recherchez un type spécifique, vous voudrez probablement faire quelque chose du genre: Object.prototype.toString.call(new FormData()) === "[object FormData]"ce qui serait vrai. Vous pouvez également utiliser slice(8, -1)pour revenir à la FormDataplace de[object FormData]
Chris Marisic

4
Y a-t-il une différence entre utiliser Object.prototypeet {}?
GetFree

3
peut-être que cela a changé au fil des ans, mais Object.prototype.toString.call(new MyCustomObject())revient [object Object]alors new MyCustomObject() instanceOf MyCustomObject returns trueque c'est ce que je voulais (Chrome 54.0.2840.99 m)
Maslow

@Maslow, j'ai rencontré le même problème que vous avez évoqué. Après avoir parcouru une documentation en ligne, j'ai fini par utiliser new MyCustomObject().constructor === MyCustomObject.
solstice333

3
Cela soulève la question, pourquoi n'ont-ils pas enveloppé ce code dans une méthode plus pratique, ou autorisé la compilation d'un opérateur supplémentaire. Je sais que vous n'êtes que le messager, mais franchement, c'est horrible.
Andrew S

60

la Object.prototype.toStringest une bonne façon, mais sa performance est le pire.

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

vérifier les performances de type js

Utilisez typeofpour résoudre un problème de base (String, Number, Boolean ...) et utilisez Object.prototype.toStringpour résoudre quelque chose de complexe (comme Array, Date, RegExp).

et voici ma solution:

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

utilisé comme:

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

Ce test sur jsPerf n'est pas tout à fait précis. Ces tests ne sont pas égaux (testant la même chose). Par exemple, typeof [] renvoie "object", typeof {} renvoie également "object", même si l'un est un objet Array et l'autre est un objet Object. Il y a beaucoup d'autres problèmes avec ce test ... Soyez prudent lorsque vous regardez jsPerf que les tests comparent des pommes à des pommes.
kmatheny

Votre typefonction est bonne, mais regardez comment elle fonctionne par rapport à d'autres typefonctions. http://jsperf.com/code-type-test-a-test
Progo

18
Ces mesures de performance doivent être tempérées avec un peu de bon sens. Bien sûr, le prototype.toString est plus lent que les autres d'un ordre de grandeur, mais dans le grand schéma des choses, il faut en moyenne quelques centaines de nanosecondes par appel. À moins que cet appel ne soit utilisé dans un chemin critique qui est très fréquemment exécuté, cela est probablement inoffensif. Je préfère avoir un code simple plutôt qu'un code qui se termine une microseconde plus rapidement.
David

({}).toString.call(obj)est plus lent que Object.prototype.toString jsperf.com/object-check-test77
timaschew

Belle solution. J'emprunte ta fonction dans ma lib :)
Dong Nguyen

19

La réponse acceptée est correcte, mais j'aime définir ce petit utilitaire dans la plupart des projets que je construis.

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

Utilisé comme ceci:

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

}

Si vous utilisez angulaire, vous pouvez même l'injecter proprement:

angular.constant('types', types);

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

Gardez une poignée sur le prototype que vous attendez de l'objet, puis comparez-le.

par exemple

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

En quoi est-ce mieux / différent que de dire o instanceof String; //true?
Jamie Treworgy

@jamietre parce que les "foo" instanceof Stringpauses
Raynos

OK, donc "typeof (o) === 'object' && o instanceof SomeObject". Il est facile de tester les chaînes. Cela semble être un travail supplémentaire, sans résoudre le problème de base consistant à savoir à l'avance ce que vous testez.
Jamie Treworgy

Désolé, cet extrait de code n'a aucun sens, mais je pense que vous savez ce que je veux dire, si vous testez des chaînes, utilisez à la typeof(x)==='string'place.
Jamie Treworgy

BTW, Object.getPrototypeOf(true)échoue là où (true).constructorrevient Boolean.
katspaugh

5

Je dirais que la plupart des solutions présentées ici souffrent d'une sur-ingénierie. Le moyen le plus simple de vérifier si une valeur est de type [object Object]est probablement de vérifier sa .constructorpropriété:

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

ou encore plus court avec les fonctions fléchées:

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

La a != nullpièce est nécessaire car on peut passer dans nullou undefinedet vous ne pouvez pas extraire une propriété de constructeur de l'un ou l'autre de ces derniers.

Il fonctionne avec n'importe quel objet créé via:

  • le Objectconstructeur
  • littéraux {}

Une autre caractéristique intéressante de celui-ci est sa capacité à donner des rapports corrects pour les classes personnalisées qui utilisent Symbol.toStringTag. Par exemple:

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

Le problème ici est que lors de l'appel Object.prototype.toStringà une instance de celui-ci, le faux rapport [object Object]sera retourné:

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

Mais la vérification par rapport au constructeur donne un résultat correct:

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

4

La meilleure façon de trouver le type REAL d'un objet (y compris le nom natif de l'objet ou du type de données (tel que String, Date, Number, ..etc) ET le type REAL d'un objet (même personnalisé); est de saisir la propriété name du constructeur du prototype d'objet:

Type natif Ex1:

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

affiche:

String

Ex2:

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

affiche:

Array

Classes personnalisées:

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

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

affiche:

CustomClass

Cela échoue si l'objet est soit nullou undefined.
Julian Knight

2

Vieille question que je connais. Vous n'avez pas besoin de le convertir. Voir cette fonction:

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;

}; 

Exemples:

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

Faible coût et simple.


Renvoie falsesi l'objet de test est nullouundefined
Julian Knight

ou trueoufalse
Julian Knight

@JulianKnight false est correct à null ou indéfini, ce n'est rien d'utile. Alors quel est le point?
Codebeat

votre exemple renvoie des données incohérentes. Certains résultats sont le type de données et d'autres la valeur false. Comment cela aide-t-il à répondre à la question?
Julian Knight le

1
@JulianKnight Voir les changements, c'est ce que vous voulez? Si vous préférez undefined ou "undefined" comme résultat, vous pouvez remplacer le dernier false si vous le souhaitez.
Codebeat le

0

J'ai mis en place un petit utilitaire de vérification de type inspiré des bonnes réponses ci-dessus:

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

exemple:

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

Ne semble pas fonctionner pour les objets qui sont nullou undefinedou trueoufalse
Julian Knight
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.