¿Cómo mostrar todos los métodos de un objeto?

249

Quiero saber cómo enumerar todos los métodos disponibles para un objeto como, por ejemplo:

 alert(show_all_methods(Math));

Esto debería imprimir:

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

Respuestas:

298

Puede usar Object.getOwnPropertyNames()para obtener todas las propiedades que pertenecen a un objeto, ya sean enumerables o no. Por ejemplo:

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

Luego puede usar filter()para obtener solo los métodos:

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

En los navegadores ES3 (IE 8 y versiones anteriores), las propiedades de los objetos integrados no son enumerables. Los objetos como windowy documentno están integrados, están definidos por el navegador y probablemente enumerables por diseño.

Desde ECMA-262 Edición 3 :

Objeto global
Hay un objeto global único (15.1), que se crea antes de que el control entre en cualquier contexto de ejecución. Inicialmente, el objeto global tiene las siguientes propiedades:

• Objetos integrados como Math, String, Date, parseInt, etc. Estos tienen atributos {DontEnum} .
• Propiedades definidas por el host adicionales. Esto puede incluir una propiedad cuyo valor es el objeto global en sí mismo; por ejemplo, en el modelo de objeto de documento HTML, la propiedad de ventana del objeto global es el objeto global en sí.

A medida que el control ingresa en contextos de ejecución, y a medida que se ejecuta el código ECMAScript, se pueden agregar propiedades adicionales al objeto global y se pueden cambiar las propiedades iniciales.

Debo señalar que esto significa que esos objetos no son propiedades enumerables del objeto Global. Si mira el resto del documento de especificación, verá que la mayoría de las propiedades y métodos integrados de estos objetos tienen el { DontEnum }atributo establecido en ellos.


Actualización: otro usuario de SO, CMS, trajo un error de IE con respecto{ DontEnum } a mi atención.

En lugar de verificar el atributo DontEnum, [Microsoft] JScript omitirá cualquier propiedad en cualquier objeto donde haya una propiedad con el mismo nombre en la cadena prototipo del objeto que tenga el atributo DontEnum.

En resumen, tenga cuidado al nombrar las propiedades de su objeto. Si hay una propiedad o método prototipo incorporado con el mismo nombre, IE lo omitirá cuando use un for...inbucle.

Andy E
fuente
Andy E, gracias por señalar esto. Claramente no estaba al tanto de esto y agradezco su esfuerzo por desenterrar esto y mencionarlo aquí. Gracias de nuevo :)
Roland Bouman
@Roland: No te preocupes. Tal vez sea un poco triste, pero tengo la especificación almacenada en mi carpeta Documentos, por lo que realmente no se requiere mucha excavación.
Andy E
¿No hay forma de obtener una lista de todos los métodos en las nuevas implementaciones de JS, entonces? ¿Como Node.js y V8? ¿Cómo hacemos los objetos de reflexión e introspección como solíamos hacerlo, como los marcos de objetos simulados, etc.? Pensé que simplemente había olvidado JS, pero creo que las cosas han cambiado con los años :)
d11wtq
2
@ d11wtq, con implementaciones de ES5, puede invocar Object.getOwnPropertyNames(), lo que devolverá incluso propiedades y métodos no enumerables.
Andy E
Dado que todos los objetos heredan de su prototipo, ¿no sería mejor hacer algo así Object.getOwnPropertyNames(Array.prototype) ?
lfender6445
71

No es posible con ES3 ya que las propiedades tienen un DontEnumatributo interno que nos impide enumerar estas propiedades. ES5, por otro lado, proporciona descriptores de propiedades para controlar las capacidades de enumeración de propiedades para que las propiedades nativas y definidas por el usuario puedan usar la misma interfaz y disfrutar de las mismas capacidades, lo que incluye poder ver las propiedades no enumerables mediante programación.

La getOwnPropertyNamesfunción se puede usar para enumerar todas las propiedades del objeto pasado, incluidas las que no se pueden enumerar. Luego, typeofse puede emplear una simple verificación para filtrar las no funciones. Desafortunadamente, Chrome es el único navegador en el que funciona actualmente.

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"]sin ningún orden en particular.

Anurag
fuente
+1 para las cosas de ES5. Se supone que IE9 es totalmente compatible con ES5, por lo que es bueno saberlo.
Andy E
1
@Andy - Microsoft está tomando IE9 muy en serio, lo que me hace feliz :)
Anurag
console.log (function (a) {return Object.getOwnPropertyNames (a) .filter (function (b) {return "function" == typeof a [b]})} (Math)); ¡Gracias!
19h
1
getOwnPropertyNames es el boleto. Incluso funciona en Nashorn. Simplemente cambiaron los nombres de los métodos del objeto Java, y pude descubrir los nuevos nombres ejecutando Object.getOwnPropertyNames (Java)
cayhorstmann
60
var methods = [];
for (var m in obj) {
    if (typeof obj[m] == "function") {
        methods.push(m);
    }
}
alert(methods.join(","));

De esta manera, obtendrá todos los métodos a los que puede recurrir obj. Esto incluye los métodos que "hereda" de su prototipo (como getMethods()en Java). Si solo desea ver los métodos definidos directamente por objusted, puede consultar con hasOwnProperty:

var methods = [];
for (var m in obj) {        
    if (typeof obj[m] == "function" && obj.hasOwnProperty(m)) {
        methods.push(m);
    }
}
alert(methods.join(","));
Roland Bouman
fuente
Sí, también me estoy dando cuenta de eso. Cuando uso algo como documento tengo windowmás suerte. Francamente, es un poco inesperado, no sé por qué no funciona para Matemáticas, etc.
Roland Bouman
44
@Roland: Es porque documenty windowson objetos con propiedades enumerables proporcionadas por el navegador, no son parte del tiempo de ejecución de secuencias de comandos. Los objetos nativos son y obviamente las propiedades no son enumerables.
Andy E
1
Cualquier E, no estoy de acuerdo, es obvio. Quiero decir, es evidente ya que parece que no podemos enumerarlos. Pero no veo la lógica de por qué esos elementos integrados deberían evitar la enumeración de sus propiedades. Solo por curiosidad, ¿hay alguna parte del estándar que diga que estos complementos no deberían tener propiedades enumerables?
Roland Bouman
@Roland: lo siento, quise decir que es obvio que no son enumerables ya que no aparecen con un for-in. Vea mi respuesta a continuación para obtener una cotización de la especificación.
Andy E
@Mic: Math es un objeto incorporado cuyas propiedades no son enumerables.
Andy E
31

El soporte de navegador más moderno console.dir(obj), que devolverá todas las propiedades de un objeto que heredó a través de su constructor. Consulte la documentación de Mozilla para obtener más información y soporte actual del navegador.

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

Las otras respuestas aquí funcionan para algo como Math, que es un objeto estático. Pero no funcionan para una instancia de un objeto, como una fecha. Encontré lo siguiente para trabajar:

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/

Esto no funcionará para algo como la pregunta original (Matemáticas), así que elija su solución según sus necesidades. Estoy publicando esto aquí porque Google me envió a esta pregunta, pero quería saber cómo hacer esto para instancias de objetos.

Dormir
fuente
3

La respuesta corta es que no puedes porque Mathy Date(fuera de mi cabeza, estoy seguro de que hay otros) no son objetos normales. Para ver esto, cree un script de prueba 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>

Verá que se presenta como un objeto de la misma manera que lo hace el documento en general, pero cuando realmente intenta ver ese objeto, ve que es un código nativo y algo que no está expuesto de la misma manera para la enumeración.

Nick Craver
fuente
1

Mathtiene un método estático donde puede llamar directamente Math.abs()mientras que Datetiene un método estático como Date.now()y también un método de instancia donde primero necesita crear una nueva instancia var time = new Date()para llamar 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);

Por supuesto, necesitará filtrar las claves obtenidas para el método estático para obtener nombres de métodos reales, porque también puede obtener length, nameque no sean una función en la lista.

Pero, ¿cómo si queremos obtener todos los métodos disponibles de la clase que extienden otra clase?
Por supuesto, tendrá que escanear a través de la raíz del prototipo como usar __proto__. Para ahorrar tiempo, puede usar la secuencia de comandos a continuación para obtener un método estático y una instancia de método profundo.

// 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 desea obtener métodos de la instancia creada, no olvide pasarla constructor.

StefansArya
fuente
0

Creo que hay una razón histórica simple por la que no puede enumerar los métodos de objetos integrados como Array, por ejemplo. Este es el por qué:

Los métodos son propiedades del prototipo-objeto, digamos Object.prototype. Eso significa que todas las instancias de objetos heredarán esos métodos. Es por eso que puede usar esos métodos en cualquier objeto. Decir .toString () por ejemplo.

Entonces, los métodos IF eran enumerables, e iteraría sobre decir {a: 123} con: "for (teclee {a: 123}) {...}" ¿qué pasaría? ¿Cuántas veces se ejecutará ese bucle?

Se iteraría una vez para la clave única 'a' en nuestro ejemplo. PERO TAMBIÉN una vez por cada propiedad enumerable de Object.prototype. Entonces, si los métodos fueran enumerables (de manera predeterminada), cualquier ciclo sobre cualquier objeto también lo haría sobre todos sus métodos heredados.

Panu Logic
fuente
1
Como las primitivas generalmente se heredan de un prototipo, esto es posible, Object.getOwnPropertyNames(Array.prototype)por ejemplo
lfender6445
¿Qué quieres decir con los métodos que son propiedades de Object.prototype? ? Cada propiedad es propiedad de Object.prototype en caso de Object
debugmode