Comment afficher toutes les méthodes d'un objet?

249

Je veux savoir comment lister toutes les méthodes disponibles pour un objet comme par exemple:

 alert(show_all_methods(Math));

Cela devrait imprimer:

abs, acos, asin, atan, atan2, ceil, cos, exp, floor, log, max, min, pow, random,round, sin, sqrt, tan, 
GeekTantra
la source

Réponses:

298

Vous pouvez utiliser Object.getOwnPropertyNames()pour obtenir toutes les propriétés qui appartiennent à un objet, qu'elles soient énumérables ou non. Par exemple:

console.log(Object.getOwnPropertyNames(Math));
//-> ["E", "LN10", "LN2", "LOG2E", "LOG10E", "PI", ...etc ]

Vous pouvez ensuite utiliser filter()pour obtenir uniquement les méthodes:

console.log(Object.getOwnPropertyNames(Math).filter(function (p) {
    return typeof Math[p] === 'function';
}));
//-> ["random", "abs", "acos", "asin", "atan", "ceil", "cos", "exp", ...etc ]

Dans les navigateurs ES3 (IE 8 et versions antérieures), les propriétés des objets intégrés ne sont pas énumérables. Les objets aiment windowet documentne sont pas intégrés, ils sont définis par le navigateur et très probablement énumérables par conception.

De l' ECMA-262 Edition 3 :

Objet global
Il existe un objet global unique (15.1), qui est créé avant que le contrôle n'entre dans un contexte d'exécution. Initialement, l'objet global a les propriétés suivantes:

• Objets intégrés tels que Math, String, Date, parseInt, etc. Ceux-ci ont des attributs {DontEnum} .
• Propriétés supplémentaires définies par l'hôte. Cela peut inclure une propriété dont la valeur est l'objet global lui-même; par exemple, dans le modèle d'objet de document HTML, la propriété window de l'objet global est l'objet global lui-même.

Lorsque le contrôle entre dans des contextes d'exécution et que le code ECMAScript est exécuté, des propriétés supplémentaires peuvent être ajoutées à l'objet global et les propriétés initiales peuvent être modifiées.

Je dois souligner que cela signifie que ces objets ne sont pas des propriétés énumérables de l'objet Global. Si vous parcourez le reste du document de spécification, vous verrez que la plupart des propriétés et méthodes intégrées de ces objets ont l' { DontEnum }attribut défini.


Mise à jour: un autre utilisateur SO, CMS, a attiré mon attention sur{ DontEnum } un bug IE .

Au lieu de vérifier l'attribut DontEnum, [Microsoft] JScript sautera toute propriété dans tout objet où il existe une propriété du même nom dans la chaîne de prototype de l'objet qui a l'attribut DontEnum.

En bref, méfiez-vous lorsque vous nommez les propriétés de votre objet. S'il existe une propriété ou une méthode de prototype intégrée du même nom, IE l'ignorera lors de l'utilisation d'une for...inboucle.

Andy E
la source
Andy E, merci de l'avoir signalé. De toute évidence, je n'étais pas au courant de cela et j'apprécie vos efforts pour creuser cela et le mentionner ici. Merci encore :)
Roland Bouman
@Roland: Pas de soucis. C'est peut-être un peu triste, mais j'ai les spécifications stockées dans mon dossier Documents, donc pas besoin de creuser beaucoup vraiment!
Andy E
N'y a-t-il donc aucun moyen d'obtenir une liste de toutes les méthodes dans les implémentations JS plus récentes? Comme Node.js et V8? Comment faisons-nous des objets de réflexion et d'introspection comme nous le faisions auparavant, comme pour les cadres d'objets fictifs, etc.? Je pensais que j'avais oublié JS, mais je suppose que les choses ont changé au fil des ans :)
d11wtq
2
@ d11wtq, avec les implémentations ES5, vous pouvez invoquer Object.getOwnPropertyNames(), qui renverra même des propriétés et des méthodes non énumérables.
Andy E
puisque tous les objets héritent de leur prototype, ne serait-il pas préférable de faire quelque chose comme ça Object.getOwnPropertyNames(Array.prototype) ?
lfender6445
71

Ce n'est pas possible avec ES3 car les propriétés ont un DontEnumattribut interne qui nous empêche d'énumérer ces propriétés. ES5, d'autre part, fournit des descripteurs de propriété pour contrôler les capacités d'énumération des propriétés afin que les propriétés définies par l'utilisateur et natives puissent utiliser la même interface et profiter des mêmes capacités, ce qui inclut la possibilité de voir les propriétés non énumérables par programme.

La getOwnPropertyNamesfonction peut être utilisée pour énumérer toutes les propriétés de l'objet transmis, y compris celles qui ne sont pas énumérables. Ensuite, une simple typeofvérification peut être utilisée pour filtrer les non-fonctions. Malheureusement, Chrome est le seul navigateur sur lequel il fonctionne actuellement.

function getAllMethods(object) {
    return Object.getOwnPropertyNames(object).filter(function(property) {
        return typeof object[property] == 'function';
    });
}

console.log(getAllMethods(Math));

se connecte ["cos", "pow", "log", "tan", "sqrt", "ceil", "asin", "abs", "max", "exp", "atan2", "random", "round", "floor", "acos", "atan", "min", "sin"]sans ordre particulier.

Anurag
la source
+1 pour le truc ES5. IE9 soutiendra censément entièrement ES5, donc ce truc est bon à savoir.
Andy E
1
@Andy - Microsoft prend IE9 très au sérieux, ce qui me rend heureux :)
Anurag
console.log (function (a) {return Object.getOwnPropertyNames (a) .filter (function (b) {return "function" == typeof a [b]})} (Math)); Je vous remercie!
19h
1
getOwnPropertyNames est le ticket. Cela fonctionne même à Nashorn. Ils ont juste changé les noms des méthodes de l'objet Java, et j'ai pu comprendre les nouveaux noms en exécutant Object.getOwnPropertyNames (Java)
cayhorstmann
60
var methods = [];
for (var m in obj) {
    if (typeof obj[m] == "function") {
        methods.push(m);
    }
}
alert(methods.join(","));

De cette façon, vous obtiendrez toutes les méthodes auxquelles vous pourrez faire appel obj. Cela inclut les méthodes qu'il "hérite" de son prototype (comme getMethods()en java). Si vous voulez seulement voir ces méthodes définies directement par objvous pouvez vérifier avechasOwnProperty :

var methods = [];
for (var m in obj) {        
    if (typeof obj[m] == "function" && obj.hasOwnProperty(m)) {
        methods.push(m);
    }
}
alert(methods.join(","));
Roland Bouman
la source
ouais, je le remarque aussi. Quand j'utilise quelque chose comme documentou windowj'ai plus de chance. Franchement c'est un peu inattendu, je ne sais pas pourquoi ça ne marche pas pour les maths etc.
Roland Bouman
4
@Roland: C'est parce que documentet windowsont des objets avec des propriétés énumérables fournies par le navigateur, ils ne font pas partie du runtime de script. Les objets natifs le sont et, évidemment, les propriétés ne sont pas énumérables.
Andy E
1
Tout E, je ne suis pas d'accord, c'est évident. Je veux dire, c'est évident puisque nous ne pouvons pas sembler les énumérer. Mais je ne vois pas la logique pour laquelle ces éléments intégrés devraient empêcher l'énumération de leurs propriétés. Juste curieux, y a-t-il une partie de la norme qui dit que ces éléments intégrés ne devraient pas avoir de propriétés énumérables?
Roland Bouman
@Roland: désolé, je voulais dire qu'il est évident qu'ils ne sont pas énumérables car ils ne se présentent pas avec un for-in. Voir ma réponse ci-dessous pour un devis de la spécification.
Andy E
@Mic: Math est un objet intégré dont les propriétés ne sont pas énumérables.
Andy E
31

Prise en charge la plus moderne du navigateur console.dir(obj), qui renverra toutes les propriétés d'un objet dont il a hérité via son constructeur. Consultez la documentation de Mozilla pour plus d'informations et la prise en charge actuelle du navigateur.

console.dir(Math)
=> MathConstructor
E: 2.718281828459045
LN2: 0.6931471805599453
...
tan: function tan() { [native code] }
__proto__: Object
Geoff Moller
la source
4

Les autres réponses ici fonctionnent pour quelque chose comme Math, qui est un objet statique. Mais ils ne fonctionnent pas pour une instance d'un objet, comme une date. J'ai trouvé ce qui suit fonctionner:

function getMethods(o) {
  return Object.getOwnPropertyNames(Object.getPrototypeOf(o))
    .filter(m => 'function' === typeof o[m])
}
//example: getMethods(new Date()):  [ 'getFullYear', 'setMonth', ... ]

https://jsfiddle.net/3xrsead0/

Cela ne fonctionnera pas pour quelque chose comme la question d'origine (Math), alors choisissez votre solution en fonction de vos besoins. Je poste ceci ici parce que Google m'a envoyé à cette question mais je voulais savoir comment le faire pour les instances d'objets.

Kip
la source
3

La réponse courte est que vous ne pouvez pas parce que Mathet Date(du haut de ma tête, je suis sûr qu'il y en a d'autres) ne sont pas des objets normaux. Pour voir cela, créez un script de test simple:

<html>
  <body>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js"></script>
    <script type="text/javascript">
      $(function() {
        alert("Math: " + Math);
        alert("Math: " + Math.sqrt);
        alert("Date: " + Date);
        alert("Array: " + Array);
        alert("jQuery: " + jQuery);
        alert("Document: " + document);
        alert("Document: " + document.ready);
      });
    </script>
  </body>
</html>

Vous voyez qu'il se présente comme un objet de la même manière que le document dans son ensemble, mais lorsque vous essayez réellement de voir dans cet objet, vous voyez que c'est du code natif et quelque chose qui n'est pas exposé de la même manière pour l'énumération.

Nick Craver
la source
1

Matha une méthode statique où vous pouvez appeler directement comme Math.abs()tout en Dateayant une méthode statique comme Date.now()et également une méthode d'instance où vous devez d'abord créer une nouvelle instance var time = new Date()à appeler time.getHours().

// The instance method of Date can be found on `Date.prototype` so you can just call:
var keys = Object.getOwnPropertyNames(Date.prototype);

// And for the static method
var keys = Object.getOwnPropertyNames(Date);

// But if the instance already created you need to
// pass its constructor
var time = new Date();
var staticKeys = Object.getOwnPropertyNames(time.constructor);
var instanceKeys = Object.getOwnPropertyNames(time.constructor.prototype);

Bien sûr, vous devrez filtrer les clés obtenues pour la méthode statique pour obtenir les noms de méthode réels, car vous pouvez également obtenir ce length, namequi n'est pas une fonction dans la liste.

Mais comment faire si nous voulons obtenir toutes les méthodes disponibles de la classe qui étendent une autre classe?
Bien sûr, vous devrez parcourir la racine du prototype comme à l'aide __proto__. Pour gagner du temps, vous pouvez utiliser le script ci-dessous pour obtenir une méthode statique et une instance de méthode approfondie.

// var keys = new Set();
function getStaticMethods(keys, clas){
    var keys2 = Object.getOwnPropertyNames(clas);

    for(var i = 0; i < keys2.length; i++){
        if(clas[keys2[i]].constructor === Function)
            keys.add(keys2[i]);
    }
}

function getPrototypeMethods(keys, clas){
    if(clas.prototype === void 0)
        return;

    var keys2 = Object.getOwnPropertyNames(clas.prototype);
    for (var i = keys2.length - 1; i >= 0; i--) {
        if(keys2[i] !== 'constructor')
            keys.add(keys2[i]);
    }

    var deep = Object.getPrototypeOf(clas);
    if(deep.prototype !== void 0)
        getPrototypeMethods(keys, deep);
}

// ====== Usage example ======
// To avoid duplicate on deeper prototype we use `Set`
var keys = new Set();
getStaticMethods(keys, Date);
getPrototypeMethods(keys, Date);

console.log(Array.from(keys));

Si vous souhaitez obtenir des méthodes à partir de l'instance créée, n'oubliez pas de la passer constructor.

StefansArya
la source
0

Je crois qu'il y a une raison historique simple pour laquelle vous ne pouvez pas énumérer les méthodes des objets intégrés comme Array par exemple. Voici pourquoi:

Les méthodes sont des propriétés du prototype-objet, par exemple Object.prototype. Cela signifie que toutes les instances d'objet hériteront de ces méthodes. C'est pourquoi vous pouvez utiliser ces méthodes sur n'importe quel objet. Dites .toString () par exemple.

Donc les méthodes IF étaient énumérables, et je répéterais en disant {a: 123} avec: "for (key in {a: 123}) {...}" que se passerait-il? Combien de fois cette boucle serait-elle exécutée?

Il serait répété une fois pour la clé unique «a» dans notre exemple. MAIS AUSSI une fois pour chaque propriété énumérable de Object.prototype. Donc, si les méthodes étaient énumérables (par défaut), alors toute boucle sur n'importe quel objet ferait également une boucle sur toutes ses méthodes héritées.

Panu Logic
la source
1
puisque les primitives héritent généralement d'un protype, cela est possible Object.getOwnPropertyNames(Array.prototype)par exemple
lfender6445
que voulez-vous dire par méthodes sont des propriétés de Object.prototype. ? Chaque propriété est une propriété de Object.prototype en cas d'objet
debugmode