Como exibir todos os métodos de um objeto?

249

Quero saber como listar todos os métodos disponíveis para um objeto, como por exemplo:

 alert(show_all_methods(Math));

Isso deve imprimir:

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

Respostas:

298

Você pode usar Object.getOwnPropertyNames()para obter todas as propriedades que pertencem a um objeto, sejam enumeráveis ​​ou não. Por exemplo:

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

Você pode usar filter()para obter apenas os métodos:

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

Nos navegadores ES3 (IE 8 e inferiores), as propriedades dos objetos internos não são enumeráveis. Objetos como windowe documentnão são incorporados, são definidos pelo navegador e provavelmente enumeráveis ​​pelo design.

Do ECMA-262 Edição 3 :

Objeto global
Há um objeto global exclusivo (15.1), criado antes do controle entrar em qualquer contexto de execução. Inicialmente, o objeto global possui as seguintes propriedades:

• Objetos internos, como Math, String, Date, parseInt, etc. Estes possuem atributos {DontEnum} .
• Propriedades adicionais definidas pelo host. Isso pode incluir uma propriedade cujo valor é o próprio objeto global; por exemplo, no modelo de objeto de documento HTML, a propriedade window do objeto global é o próprio objeto global.

À medida que o controle entra nos contextos de execução e quando o código ECMAScript é executado, propriedades adicionais podem ser adicionadas ao objeto global e as propriedades iniciais podem ser alteradas.

Devo salientar que isso significa que esses objetos não são propriedades enumeráveis ​​do objeto Global. Se você examinar o restante do documento de especificação, verá que a maioria das propriedades e métodos internos desses objetos têm o { DontEnum }atributo definido neles.


Atualização: um colega usuário do SO, CMS, trouxe um bug do IE referente{ DontEnum } à minha atenção.

Em vez de verificar o atributo DontEnum, o JScript da [Microsoft] pulará qualquer propriedade em qualquer objeto em que haja uma propriedade com o mesmo nome na cadeia de protótipos do objeto que possui o atributo DontEnum.

Em resumo, tenha cuidado ao nomear as propriedades do seu objeto. Se houver uma propriedade ou método de protótipo interno com o mesmo nome, o IE o ignorará ao usar um for...inloop.

Andy E
fonte
Andy E, obrigado por apontar isso. Claramente, eu não estava ciente disso e agradeço seu esforço em desenterrar isso e mencioná-lo aqui. Obrigado novamente :)
Roland Bouman
@Roland: Não se preocupe. Talvez seja um pouco triste, mas eu tenho as especificações armazenadas na minha pasta Documentos, então não é necessário cavar muito!
Andy E
Não há como obter uma lista de todos os métodos nas implementações JS mais recentes? Como o Node.js e o V8? Como fazemos a reflexão e a introspecção de objetos como costumávamos fazer, como estruturas de objetos simuladas etc.? Pensei apenas esquecido JS, mas eu acho que as coisas mudaram ao longo dos anos :)
d11wtq
2
@ d11wtq, com implementações do ES5, você pode chamar Object.getOwnPropertyNames(), que retornará propriedades e métodos não enumeráveis.
Andy E
como todos os objetos herdam de seu protótipo, não seria melhor fazer algo assim Object.getOwnPropertyNames(Array.prototype) ?
Lfender6445
71

Não é possível no ES3, pois as propriedades possuem um DontEnumatributo interno que nos impede de enumerar essas propriedades. O ES5, por outro lado, fornece descritores de propriedades para controlar os recursos de enumeração de propriedades, para que propriedades nativas e definidas pelo usuário possam usar a mesma interface e desfrutar dos mesmos recursos, o que inclui a capacidade de ver propriedades não enumeráveis ​​programaticamente.

A getOwnPropertyNamesfunção pode ser usada para enumerar todas as propriedades do objeto passado, incluindo aquelas que não são enumeráveis. Em seguida, uma typeofverificação simples pode ser empregada para filtrar as não funções. Infelizmente, o Chrome é o único navegador em que ele trabalha atualmente.

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

console.log(getAllMethods(Math));

logs ["cos", "pow", "log", "tan", "sqrt", "ceil", "asin", "abs", "max", "exp", "atan2", "random", "round", "floor", "acos", "atan", "min", "sin"]em nenhuma ordem específica.

Anurag
fonte
+1 para o material ES5. O IE9 supostamente suporta totalmente o ES5, portanto é bom saber disso.
Andy E
1
@Andy - Microsoft está tomando IE9 muito a sério o que me faz feliz :)
Anurag
console.log (function (a) {return Object.getOwnPropertyNames (a) .filter (função (b) {return "function" == typeof a [b]})} (Math); Obrigado!
19h
1
getOwnPropertyNames é o ticket. Até funciona em Nashorn. Eles apenas mudaram os nomes dos métodos do objeto Java, e eu era capaz de descobrir os novos nomes por Object.getOwnPropertyNames em execução (Java)
cayhorstmann
60
var methods = [];
for (var m in obj) {
    if (typeof obj[m] == "function") {
        methods.push(m);
    }
}
alert(methods.join(","));

Dessa forma, você obterá todos os métodos que você pode chamar obj. Isso inclui os métodos que "herda" de seu protótipo (como getMethods()em java). Se você quiser apenas ver os métodos definidos diretamente por objvocê, verifique com hasOwnProperty:

var methods = [];
for (var m in obj) {        
    if (typeof obj[m] == "function" && obj.hasOwnProperty(m)) {
        methods.push(m);
    }
}
alert(methods.join(","));
Roland Bouman
fonte
Sim, eu também estou percebendo isso. Quando uso algo parecido documentou windowtenho mais sorte. Francamente, é um pouco inesperado, eu não sei por que ele não funciona para Math etc.
Roland Bouman
4
@Roland: é porque documente windowsão objetos com propriedades enumeráveis ​​fornecidas pelo navegador, eles não fazem parte do tempo de execução do script. Objetos nativos são e obviamente as propriedades não são enumeráveis.
Andy E
1
Qualquer E, eu não concordo, é óbvio. Quero dizer, é aparente, já que não conseguimos enumerá-las. Mas não vejo a lógica do motivo pelo qual esses internos devem impedir a enumeração de suas propriedades. Apenas curioso, há alguma parte do padrão que diz que esses internos não devem ter propriedades enumeráveis?
Roland Bouman
@Roland: desculpe, eu quis dizer que é óbvio que eles não são enumeráveis, pois não aparecem com um for-in. Veja minha resposta abaixo para obter uma cotação das especificações.
Andy E
@Mic: Math é um objeto interno cujas propriedades não são enumeráveis.
Andy E
31

O suporte ao navegador mais moderno console.dir(obj), que retornará todas as propriedades de um objeto que ele herdou por meio de seu construtor. Consulte a documentação do Mozilla para obter mais informações e suporte atual ao navegador.

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

As outras respostas aqui funcionam para algo como Math, que é um objeto estático. Mas eles não funcionam para uma instância de um objeto, como uma data. Eu encontrei o seguinte para trabalhar:

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/

Isso não funcionará para algo como a pergunta original (matemática); portanto, escolha sua solução com base nas suas necessidades. Estou postando isso aqui porque o Google me enviou a essa pergunta, mas eu queria saber como fazer isso para instâncias de objetos.

Kip
fonte
3

A resposta curta é que você não pode, porque Mathe Date(em cima da minha cabeça, tenho certeza que existem outros) não são objetos normais. Para ver isso, crie um script de teste simples:

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

Você vê que ele apresenta como um objeto da mesma maneira que o documento em geral, mas quando você realmente tenta ver nesse objeto, vê que é código nativo e algo não exposto da mesma maneira para enumeração.

Nick Craver
fonte
1

Mathtem um método estático onde você pode chamar diretamente like Math.abs()enquanto Datetem um método estático Date.now()e também um método de instância em que é necessário criar uma nova instância primeiro var time = new Date()para chamar 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);

É claro que você precisará filtrar as chaves obtidas para o método estático para obter nomes de métodos reais, porque você também pode obter length, nameque não sejam uma função na lista.

Mas como se queremos obter todo método disponível da classe que estende outra classe?
Claro que você precisará escanear a raiz do protótipo como usar __proto__. Para economizar seu tempo, você pode usar o script abaixo para obter o método estático e a instância profunda do método.

// 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 você deseja obter métodos da instância criada, não esqueça de passar a instância constructor.

StefansArya
fonte
0

Acredito que há uma simples razão histórica pela qual você não pode enumerar métodos de objetos internos, como Array, por exemplo. Aqui está o porquê:

Métodos são propriedades do objeto-protótipo, como Object.prototype. Isso significa que todas as instâncias de objetos herdarão esses métodos. É por isso que você pode usar esses métodos em qualquer objeto. Diga .toString (), por exemplo.

Portanto, se os métodos fossem enumeráveis ​​e eu repetiria digitando {a: 123} com: "for (digite {a: 123}) {...}" o que aconteceria? Quantas vezes esse loop seria executado?

Seria iterado uma vez para a chave única 'a' em nosso exemplo. MAS TAMBÉM uma vez para cada propriedade enumerável de Object.prototype. Portanto, se os métodos fossem enumeráveis ​​(por padrão), qualquer loop sobre qualquer objeto também repetiria todos os seus métodos herdados.

Panu Logic
fonte
1
desde primitivos geralmente herdam de uma ProType, isso é possível Object.getOwnPropertyNames(Array.prototype), por exemplo
lfender6445
o que você quer dizer com métodos são propriedades de Object.prototype. ? Cada propriedade é propriedades de Object.prototype em caso de Objeto
debugmode