Jak wyświetlić wszystkie metody obiektu?

249

Chcę wiedzieć, jak wyświetlić listę wszystkich metod dostępnych dla obiektu, takich jak na przykład:

 alert(show_all_methods(Math));

To powinno wydrukować:

abs, acos, asin, atan, atan2, ceil, cos, exp, floor, log, max, min, pow, random,round, sin, sqrt, tan, 
GeekTantra
źródło

Odpowiedzi:

298

Możesz użyć, Object.getOwnPropertyNames()aby uzyskać wszystkie właściwości należące do obiektu, bez względu na to, czy są one wyliczalne, czy nie. Na przykład:

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

Następnie można użyć, filter()aby uzyskać tylko metody:

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

W przeglądarkach ES3 (IE 8 i niższych) właściwości wbudowanych obiektów nie są wyliczalne. Obiekty takie jak windowi documentnie są wbudowane, są zdefiniowane przez przeglądarkę i najprawdopodobniej są policzalne według projektu.

Z ECMA-262 wydanie 3 :

Obiekt globalny
Istnieje unikalny obiekt globalny (15.1), który jest tworzony, zanim kontrola wejdzie w kontekst wykonywania. Początkowo obiekt globalny ma następujące właściwości:

• Wbudowane obiekty, takie jak Math, String, Date, parsInt itp. Mają atrybuty {DontEnum} .
• Dodatkowe właściwości zdefiniowane przez hosta. Może to obejmować właściwość, której wartością jest sam obiekt globalny; na przykład w modelu obiektu dokumentu HTML właściwością window obiektu globalnego jest sam obiekt globalny.

Gdy kontrola wchodzi w konteksty wykonawcze i podczas wykonywania kodu ECMAScript, do obiektu globalnego można dodawać dodatkowe właściwości, a właściwości początkowe można zmieniać.

Powinienem zaznaczyć, że oznacza to, że te obiekty nie są policzalnymi właściwościami obiektu Global. Jeśli przejrzysz resztę dokumentu specyfikacji, zobaczysz, że większość wbudowanych właściwości i metod tych obiektów ma { DontEnum }ustawiony atrybut.


Aktualizacja: inny użytkownik SO, CMS, zwrócił moją uwagę na błąd IE{ DontEnum } .

Zamiast sprawdzania atrybutu DontEnum, [Microsoft] JScript pominie dowolną właściwość w dowolnym obiekcie, w którym w łańcuchu prototypów obiektu ma właściwość o tej samej nazwie, która ma atrybut DontEnum.

Krótko mówiąc, strzeż się, nazywając właściwości obiektu. Jeśli istnieje wbudowana właściwość lub metoda prototypu o tej samej nazwie, IE pominie ją podczas korzystania z for...inpętli.

Andy E.
źródło
Andy E., dzięki za zwrócenie na to uwagi. Najwyraźniej nie byłem tego świadomy i doceniam twój wysiłek w wykopaniu tego i wspomnieniu o tym tutaj. Jeszcze raz dziękuję :)
Roland Bouman
@Roland: Bez obaw. Może to trochę smutne, ale mam specyfikację zapisaną w folderze Dokumenty, więc naprawdę nie trzeba dużo kopać!
Andy E
Czy nie ma zatem sposobu na uzyskanie listy wszystkich metod w nowszych implementacjach JS? Jak Node.js i V8? Jak robimy obiekty odbicia i introspekcji, tak jak kiedyś, na przykład w przypadku fałszywych struktur obiektów itp.? Myślałem, że właśnie zapomniałem o JS, ale myślę, że przez lata wszystko się zmieniło :)
d11wtq
2
@ d11wtq, z implementacjami ES5, możesz wywołać Object.getOwnPropertyNames(), co zwróci nawet niepoliczalne właściwości i metody.
Andy E.
skoro wszystkie obiekty dziedziczą po prototypie, czy nie lepiej byłoby coś takiego zrobić Object.getOwnPropertyNames(Array.prototype) ?
lfender6445
71

W ES3 nie jest to możliwe, ponieważ właściwości mają wewnętrzny DontEnumatrybut, który uniemożliwia nam wyliczenie tych właściwości. Z drugiej strony ES5 zapewnia deskryptory właściwości do kontrolowania możliwości wyliczania właściwości, dzięki czemu właściwości zdefiniowane przez użytkownika i właściwości rodzime mogą korzystać z tego samego interfejsu i cieszyć się tymi samymi możliwościami, co obejmuje możliwość programowego wyświetlania właściwości niepoliczalnych.

Tej getOwnPropertyNamesfunkcji można użyć do wyliczenia wszystkich właściwości przekazanego obiektu, w tym tych, których nie można wyliczyć. Następnie można zastosować prostą typeofkontrolę, aby odfiltrować niefunkcje. Niestety, Chrome jest jedyną przeglądarką, w której obecnie działa.

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

console.log(getAllMethods(Math));

loguje się ["cos", "pow", "log", "tan", "sqrt", "ceil", "asin", "abs", "max", "exp", "atan2", "random", "round", "floor", "acos", "atan", "min", "sin"]w określonej kolejności.

Anurag
źródło
+1 za rzeczy ES5. IE9 podobno w pełni obsługuje ES5, więc warto wiedzieć o tych rzeczach.
Andy E,
1
@Andy - Microsoft bardzo poważnie podchodzi do IE9, co mnie cieszy :)
Anurag
console.log (function (a) {return Object.getOwnPropertyNames (a) .filter (function (b) {return "function" == typeof a [b]})} (Math)); Dziękuję Ci!
19h
1
getOwnPropertyNames jest biletem. Działa nawet w Nashorn. Właśnie zmienili nazwy metod obiektu Java, a ja mogłem znaleźć nowe nazwy, uruchamiając Object.getOwnPropertyNames (Java)
cayhorstmann
60
var methods = [];
for (var m in obj) {
    if (typeof obj[m] == "function") {
        methods.push(m);
    }
}
alert(methods.join(","));

W ten sposób otrzymasz wszystkie metody, które możesz wywołać obj. Obejmuje to metody, które „dziedziczy” po prototypie (jak getMethods()w Javie). Jeśli chcesz zobaczyć tylko te metody zdefiniowane bezpośrednio przez objsiebie, możesz to sprawdzić za pomocą hasOwnProperty:

var methods = [];
for (var m in obj) {        
    if (typeof obj[m] == "function" && obj.hasOwnProperty(m)) {
        methods.push(m);
    }
}
alert(methods.join(","));
Roland Bouman
źródło
tak, też to zauważam. Kiedy używam czegoś takiego documentlub windowmam więcej szczęścia. Szczerze mówiąc, jest to trochę nieoczekiwane, nie wiem, dlaczego to nie działa dla matematyki itp.
Roland Bouman,
4
@Roland: To dlatego, że documenti windowsą obiektami o właściwościach przeliczalnych świadczonych przez przeglądarkę, nie są one częścią wykonywania skryptów. Obiekty rodzime są i oczywiście właściwości nie są policzalne.
Andy E
1
Jakiekolwiek E, nie zgadzam się, to oczywiste. To znaczy, widać, że nie możemy ich wymienić. Ale nie widzę logiki, dlaczego te wbudowane funkcje powinny zapobiegać wyliczaniu ich właściwości. Ciekawe, czy jest jakaś część standardu, która mówi, że te wbudowane nie powinny mieć wyliczalnych właściwości?
Roland Bouman
@Roland: przepraszam, miałem na myśli, że to oczywiste, że nie można ich wyliczyć, ponieważ nie pojawiają się przy for-in. Zobacz moją odpowiedź poniżej, aby uzyskać cytat ze specyfikacji.
Andy E
@Mic: Math to wbudowany obiekt, którego właściwości nie można wyliczyć.
Andy E
31

Najnowocześniejsza obsługa przeglądarki console.dir(obj), która zwróci wszystkie właściwości obiektu odziedziczonego przez konstruktor. Zobacz dokumentację Mozilli, aby uzyskać więcej informacji i bieżącą obsługę przeglądarki.

console.dir(Math)
=> MathConstructor
E: 2.718281828459045
LN2: 0.6931471805599453
...
tan: function tan() { [native code] }
__proto__: Object
Geoff Moller
źródło
4

Inne odpowiedzi tutaj działają dla czegoś takiego jak matematyka, która jest obiektem statycznym. Ale nie działają one dla instancji obiektu, na przykład daty. Znalazłem następujące do pracy:

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/

To nie zadziała dla czegoś takiego jak oryginalne pytanie (matematyka), więc wybierz swoje rozwiązanie w oparciu o twoje potrzeby. Publikuję to tutaj, ponieważ Google wysłało mnie na to pytanie, ale chciałem wiedzieć, jak to zrobić w przypadku instancji obiektów.

Wyrko
źródło
3

Krótka odpowiedź brzmi: nie możesz, ponieważ Mathi Date(z góry mojej głowy, jestem pewien, że istnieją inne) nie są normalnymi przedmiotami. Aby to zobaczyć, utwórz prosty skrypt testowy:

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

Widzisz, że przedstawia się jako obiekt w taki sam sposób, jak robi to ogólnie dokument, ale kiedy faktycznie próbujesz zobaczyć w tym obiekcie, widzisz, że jest to kod macierzysty i coś, co nie zostało ujawnione w ten sam sposób dla wyliczenia.

Nick Craver
źródło
1

Mathma metodę statyczną, w której można wywoływać bezpośrednio jak, Math.abs()podczas gdy Datema metodę statyczną podobną, Date.now()a także metodę instancji, w której należy najpierw utworzyć nową instancję, var time = new Date()aby wywołać 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);

Oczywiście będziesz musiał przefiltrować uzyskane klucze dla metody statycznej, aby uzyskać rzeczywiste nazwy metod, ponieważ możesz również uzyskać length, name, które nie są funkcją na liście.

Ale jeśli chcemy uzyskać wszystkie dostępne metody z klasy, która rozszerza inną klasę?
Oczywiście będziesz musiał zeskanować katalog główny prototypu, np __proto__. Używając . Aby zaoszczędzić czas, możesz użyć poniższego skryptu, aby uzyskać metodę statyczną i metodę głębokiej instancji.

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

Jeśli chcesz uzyskać metody z utworzonej instancji, nie zapomnij przekazać constructorjej.

StefansArya
źródło
0

Uważam, że istnieje prosty historyczny powód, dla którego nie można wyliczyć metod wbudowanych obiektów, takich jak na przykład Array. Dlatego:

Metody są właściwościami obiektu prototypowego, powiedzmy Object.prototype. Oznacza to, że wszystkie instancje Object odziedziczą te metody. Dlatego możesz używać tych metod na dowolnym obiekcie. Powiedz na przykład .toString ().

Tak więc, jeśli metody IF byłyby policzalne, powtórzyłbym powiedz {a: 123} za pomocą: "for (key in {a: 123}) {...}" co by się stało? Ile razy ta pętla byłaby wykonywana?

W naszym przykładzie byłoby to powtórzone raz dla pojedynczego klucza „a”. ALE RÓWNIEŻ raz dla każdej wyliczalnej właściwości Object.prototype. Więc jeśli metody byłyby wyliczalne (domyślnie), wówczas każda pętla nad dowolnym obiektem również zapętlałaby wszystkie swoje odziedziczone metody.

Panu Logic
źródło
1
ponieważ prymitywy zwykle dziedziczą po protype, jest to możliwe Object.getOwnPropertyNames(Array.prototype)na przykład
lfender6445
co masz na myśli, metody są właściwościami Object.prototype. ? Każda właściwość ma właściwości Object.prototype w przypadku Object
debugmode,