Come visualizzare tutti i metodi di un oggetto?


249

Voglio sapere come elencare tutti i metodi disponibili per un oggetto come ad esempio:

 alert(show_all_methods(Math));

Questo dovrebbe stampare:

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

Risposte:


298

È possibile utilizzare Object.getOwnPropertyNames()per ottenere tutte le proprietà che appartengono a un oggetto, sia enumerabili che no. Per esempio:

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

È quindi possibile utilizzare filter()per ottenere solo i metodi:

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

Nei browser ES3 (IE 8 e precedenti), le proprietà degli oggetti incorporati non sono enumerabili. Gli oggetti come windowe documentnon sono integrati, sono definiti dal browser e molto probabilmente enumerabili in base alla progettazione.

Dall'edizione 3 di ECMA-262 :

Oggetto globale
Esiste un oggetto globale univoco (15.1), che viene creato prima che il controllo entri in qualsiasi contesto di esecuzione. Inizialmente l'oggetto globale ha le seguenti proprietà:

• Oggetti incorporati come Math, String, Date, parseInt, ecc. Questi hanno attributi {DontEnum} .
• Ulteriori proprietà definite dall'host. Ciò può includere una proprietà il cui valore è l'oggetto globale stesso; ad esempio, nel modello a oggetti del documento HTML la proprietà window dell'oggetto globale è l'oggetto globale stesso.

Quando il controllo entra in contesti di esecuzione e quando viene eseguito il codice ECMAScript, è possibile aggiungere proprietà aggiuntive all'oggetto globale e le proprietà iniziali possono essere modificate.

Devo sottolineare che questo significa che quegli oggetti non sono proprietà enumerabili dell'oggetto Global. Se guardi il resto del documento di specifica, vedrai che la maggior parte delle proprietà e dei metodi incorporati di questi oggetti hanno l' { DontEnum }attributo impostato su di essi.


Aggiornamento: un altro utente SO, CMS, ha portato alla mia attenzione un bug IE{ DontEnum } .

Invece di controllare l'attributo DontEnum, [Microsoft] JScript salterà qualsiasi proprietà in qualsiasi oggetto in cui è presente una proprietà con lo stesso nome nella catena di prototipi dell'oggetto che ha l'attributo DontEnum.

In breve, fai attenzione quando assegni un nome alle proprietà del tuo oggetto. Se esiste una proprietà o un metodo prototipo incorporato con lo stesso nome, IE lo salterà quando utilizza un for...inciclo.


Andy E, grazie per averlo segnalato. Chiaramente non ne ero a conoscenza e apprezzo il tuo sforzo per scavare e menzionarlo qui. Grazie ancora :)
Roland Bouman,

@Rolanda: nessuna preoccupazione. Forse è un po 'triste, ma ho le specifiche memorizzate nella mia cartella Documenti, quindi non è necessario scavare molto!
Andy E,

Quindi non c'è modo di ottenere un elenco di tutti i metodi nelle più recenti implementazioni JS? Come Node.js e V8? Come facciamo gli oggetti di riflessione e introspettivi come facevamo prima, come per i framework di oggetti finti ecc? Pensavo di aver appena dimenticato JS, ma immagino che le cose siano cambiate nel corso degli anni :)
d11wtq

2
@ d11wtq, con implementazioni ES5, è possibile richiamare Object.getOwnPropertyNames(), che restituirà anche proprietà e metodi non enumerabili.
Andy E

dal momento che tutti gli oggetti ereditano dal loro prototipo, non sarebbe meglio fare qualcosa del genere Object.getOwnPropertyNames(Array.prototype) ?
lfender6445,

71

Non è possibile con ES3 poiché le proprietà hanno un DontEnumattributo interno che ci impedisce di enumerare queste proprietà. ES5, d'altra parte, fornisce descrittori di proprietà per il controllo delle capacità di enumerazione delle proprietà, in modo che le proprietà definite dall'utente e native possano usare la stessa interfaccia e godere delle stesse capacità, tra cui la possibilità di vedere le proprietà non enumerabili a livello di codice.

La getOwnPropertyNamesfunzione può essere utilizzata per enumerare tutte le proprietà dell'oggetto passato, comprese quelle non enumerabili. Quindi è typeofpossibile utilizzare un semplice controllo per filtrare le non funzioni. Sfortunatamente, Chrome è l'unico browser su cui funziona attualmente.

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

console.log(getAllMethods(Math));

registra ["cos", "pow", "log", "tan", "sqrt", "ceil", "asin", "abs", "max", "exp", "atan2", "random", "round", "floor", "acos", "atan", "min", "sin"]in nessun ordine particolare.


+1 per la roba ES5. Presumibilmente IE9 supporterà completamente ES5, quindi è bene sapere queste cose.
Andy E

1
@Andy - Microsoft sta prendendo molto sul serio IE9, il che mi rende felice :)
Anurag,

console.log (function (a) {return Object.getOwnPropertyNames (a) .filter (function (b) {return "function" == typeof a [b]})} (Math)); Grazie!
19h,

1
getOwnPropertyNames è il biglietto. Funziona anche a Nashorn. Hanno appena cambiato i nomi dei metodi dell'oggetto Java e sono stato in grado di capire i nuovi nomi eseguendo Object.getOwnPropertyNames (Java)
cayhorstmann

60
var methods = [];
for (var m in obj) {
    if (typeof obj[m] == "function") {
        methods.push(m);
    }
}
alert(methods.join(","));

In questo modo, otterrai tutti i metodi su cui puoi fare affidamento obj. Ciò include i metodi che "eredita" dal suo prototipo (come getMethods()in Java). Se vuoi vedere solo quei metodi definiti direttamente da objpuoi verificare con hasOwnProperty:

var methods = [];
for (var m in obj) {        
    if (typeof obj[m] == "function" && obj.hasOwnProperty(m)) {
        methods.push(m);
    }
}
alert(methods.join(","));

sì, lo sto notando anche io. Quando uso qualcosa del genere documento windowho più fortuna. Francamente è un po 'inaspettato, non so perché non funzioni per la matematica, ecc.
Roland Bouman,

4
@Roland: è perché documente windowsono oggetti con proprietà enumerabili fornite dal browser, non fanno parte del runtime degli script. Gli oggetti nativi sono e ovviamente le proprietà non sono enumerabili.
Andy E,

1
Qualsiasi E, non sono d'accordo è ovvio. Voglio dire, è evidente dal momento che non possiamo sembrare elencarli. Ma non vedo la logica del perché quei built-in dovrebbero impedire l'enumerazione delle loro proprietà. Solo curioso, c'è qualche parte dello standard che dice che questi built-in non dovrebbero avere proprietà enumerabili?
Roland Bouman,

@Roland: scusa, volevo dire che è ovvio che non sono enumerabili dal momento che non si presentano con un for-in. Vedi la mia risposta qui sotto per un preventivo dalle specifiche.
Andy E,

@Mic: la matematica è un oggetto incorporato le cui proprietà non sono enumerabili.
Andy E,

31

Supporto browser più moderno console.dir(obj), che restituirà tutte le proprietà di un oggetto che ha ereditato tramite il suo costruttore. Consulta la documentazione di Mozilla per maggiori informazioni e il supporto del browser corrente.

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

4

Le altre risposte qui funzionano per qualcosa come Math, che è un oggetto statico. Ma non funzionano per un'istanza di un oggetto, come una data. Ho trovato il seguente per funzionare:

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/

Questo non funzionerà per qualcosa come la domanda originale (matematica), quindi scegli la tua soluzione in base alle tue esigenze. Sto pubblicando questo qui perché Google mi ha inviato a questa domanda ma volevo sapere come farlo per le istanze di oggetti.


3

La risposta breve è che non può perché Mathe Date(la parte superiore della mia testa, sono sicuro che ci sono altri) are't oggetti normali. Per vedere questo, crea un semplice script di test:

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

Vedete che si presenta come un oggetto nello stesso modo in cui lo fa il documento, ma quando in realtà provate a vedere in quell'oggetto, vedete che è un codice nativo e qualcosa non esposto allo stesso modo per l'enumerazione.


1

Mathha un metodo statico in cui è possibile chiamare direttamente come Math.abs()mentre Dateha un metodo statico come Date.now()e anche un metodo di istanza in cui è necessario creare prima una nuova istanza var time = new Date()per chiamare 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);

Ovviamente dovrai filtrare le chiavi ottenute per il metodo statico per ottenere i nomi dei metodi effettivi, perché puoi anche ottenere length, nameche non sono una funzione nell'elenco.

Ma se vogliamo ottenere tutti i metodi disponibili dalla classe che estendono un'altra classe?
Ovviamente dovrai scansionare la radice del prototipo come usando __proto__. Per risparmiare tempo, puoi utilizzare lo script seguente per ottenere un metodo statico e un'istanza di metodo approfondita.

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

Se si desidera ottenere metodi dall'istanza creata, non dimenticare di passarne constructor.


0

Credo che ci sia una semplice ragione storica per cui non è possibile elencare metodi di oggetti incorporati come Array, ad esempio. Ecco perché:

I metodi sono proprietà dell'oggetto prototipo, ad esempio Object.prototype. Ciò significa che tutte le istanze di oggetti erediteranno tali metodi. Ecco perché puoi usare questi metodi su qualsiasi oggetto. Dire ad esempio .toString ().

Quindi i metodi IF erano enumerabili e avrei ripetuto dicendo {a: 123} con: "for (digita {a: 123}) {...}" che cosa sarebbe successo? Quante volte verrà eseguito quel loop?

Verrà ripetuto una volta per la singola chiave "a" nel nostro esempio. MA ANCHE una volta per ogni proprietà enumerabile di Object.prototype. Quindi, se i metodi fossero enumerabili (per impostazione predefinita), allora qualsiasi ciclo su qualsiasi oggetto passerebbe anche su tutti i suoi metodi ereditati.


1
poiché i primitivi solitamente ereditano da un prototipo, questo è possibile Object.getOwnPropertyNames(Array.prototype)ad esempio
lfender6445

cosa intendi con metodi sono proprietà di Object.prototype. ? Ogni proprietà è proprietà di Object.prototype in caso di Object
debugmode
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.