Як відобразити всі методи об’єкта?


249

Я хочу знати, як перелічити всі доступні для об’єкта методи, наприклад, наприклад:

 alert(show_all_methods(Math));

Це має надрукувати:

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

Відповіді:


298

Ви можете використовувати Object.getOwnPropertyNames()для отримання всіх властивостей, що належать об'єкту, чисельних чи ні. Наприклад:

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

Потім ви можете використовувати filter()лише методи:

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

У браузерах ES3 (IE 8 і новіші) властивості вбудованих об'єктів не перелічуються. Об'єкти на кшталт windowі documentне вбудовані, вони визначаються браузером і, швидше за все, перераховані за дизайном.

З версії 3 ECMA-262 :

Глобальний об'єкт
Існує унікальний глобальний об'єкт (15.1), який створюється до того, як контроль вступить у будь-який контекст виконання. Спочатку глобальний об'єкт має такі властивості:

• Вбудовані об'єкти, такі як Math, String, Date, parseInt тощо. Вони мають атрибути {DontEnum} .
• Додаткові властивості, визначені хостом. Це може включати властивість, значенням якої є сам глобальний об'єкт; наприклад, в моделі об’єкта документа HTML властивістю вікна глобального об'єкта є сам глобальний об'єкт.

Коли управління вводить контексти виконання, і коли виконується код ECMAScript, до глобального об'єкта можуть бути додані додаткові властивості, а початкові властивості можуть бути змінені.

Я мушу зазначити, що це означає, що ці об'єкти не перелічують властивості об'єкта Global. Якщо ви переглянете решту документа специфікації, ви побачите, що більшість вбудованих властивостей і методів цих об'єктів мають на них { DontEnum }атрибут.


Оновлення: інший користувач SO, CMS, звернув увагу на помилку IE{ DontEnum } .

Замість перевірки атрибута DontEnum [Microsoft] JScript буде пропускати будь-яку властивість у будь-якому об'єкті, де в ланцюзі прототипу об'єкта є однойменне властивість, яке має атрибут DontEnum.

Коротше кажучи, будьте обережні, називаючи свої об'єкти. Якщо є вбудована властивість прототипу або метод з тим самим іменем, то IE буде пропускати його під час використання for...inциклу.


Енді Е, дякую, що вказав на це. Зрозуміло, що я цього не знав, і я ціную ваші зусилля, щоб розкопати це та згадати тут. Ще раз дякую :)
Roland Bouman

@Roland: Не хвилюйся. Можливо, це трохи сумно, але у мене специфікація зберігається в моїй папці «Документи», тому не потрібно дуже багато копати!
Енді Е

Тоді немає ніякого способу отримати список усіх методів у новіших реалізаціях JS? Як Node.js і V8? Як ми можемо робити відображення та інтроспективні об'єкти, як ми це робили, наприклад, для макетних рам об'єктів тощо? Я думав, що щойно забув JS, але, мабуть, все змінилося з роками :)
d11wtq

2
@ d11wtq, з реалізаціями ES5 ви можете викликати Object.getOwnPropertyNames(), що поверне навіть не перелічені властивості та методи.
Енді Е

оскільки всі об'єкти успадковуються від прототипу, чи не було б краще зробити щось на кшталт Object.getOwnPropertyNames(Array.prototype) ?
lfender6445

71

З ES3 це неможливо, оскільки властивості мають внутрішній DontEnumатрибут, який заважає нам перераховувати ці властивості. ES5, з іншого боку, надає дескриптори властивостей для управління можливостями перерахування властивостей, щоб визначені користувачем і нативними властивостями могли використовувати той самий інтерфейс і користуватися тими ж можливостями, що включає можливість перегляду нечисленних властивостей програмно.

getOwnPropertyNamesФункція може бути використана для перерахування над усіма властивостями переданих в об'єкті, в тому числі і ті , які не є перелічуваних. Тоді typeofможе бути використана проста перевірка для фільтрації нефункціональних функцій. На жаль, Chrome - це єдиний браузер, над яким він працює зараз.

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

console.log(getAllMethods(Math));

журнали ["cos", "pow", "log", "tan", "sqrt", "ceil", "asin", "abs", "max", "exp", "atan2", "random", "round", "floor", "acos", "atan", "min", "sin"]в конкретному порядку.


+1 для матеріалів ES5. IE9 буде ніби повністю підтримувати ES5, тому цей матеріал добре знати.
Енді Е

1
@Andy - Microsoft сприймає IE9 дуже серйозно, що робить мене щасливим :)
Anurag

console.log (function (a) {return Object.getOwnPropertyNames (a) .filter (function (b) {return "function" == typeof a [b]})} (Math)); Дякую!
19

1
getOwnPropertyNames - це квиток. Це працює навіть у Нашорні. Вони просто змінили назви методів об’єкта Java, і я зміг з'ясувати нові імена, запустивши Object.getOwnPropertyNames (Java)
cayhorstmann

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

Таким чином, ви отримаєте всі методи, до яких можна звернутися obj. Сюди входять методи, які він «успадковує» від свого прототипу (як getMethods()у java). Якщо ви хочете бачити лише ті методи, які визначені безпосередньо, objви можете перевірити hasOwnProperty:

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

так, я теж це помічаю. Коли я використовую щось подібне documentабо windowя отримую більше удачі. Відверто кажучи, це трохи несподівано, я не знаю, чому це не працює для Math і т. Д.
Roland Bouman

4
@Roland: Це тому , що documentі windowоб'єкти з перелічуваних властивостей , що надаються браузером, вони не є частиною сценаріїв виконання. Рідні об'єкти є і, очевидно, властивостей не перелічити.
Енді Е

1
Будь-який E, я не згоден, це очевидно. Я маю на увазі, це очевидно, оскільки ми не можемо їх перерахувати. Але я не бачу логіки, чому ці вбудовані пристрої повинні перешкоджати перерахуванню їх властивостей. Цікаво, чи є якась частина стандарту, яка говорить про те, що ці вбудовані пристрої не повинні мати численні властивості?
Roland Bouman

@Roland: вибачте, я мав на увазі, що очевидно, що їх не перелічують, оскільки вони не з’являються із заявкою. Дивіться мою відповідь нижче, щоб побачити цитату із специфікації.
Енді Е

@Mic: Math - це вбудований об'єкт, властивості якого не перелічені.
Енді Е

31

Більшість сучасної підтримки браузера console.dir(obj), яка поверне всі властивості об'єкта, який він успадкував через свій конструктор. Щоб отримати докладнішу інформацію та поточну підтримку браузера, перегляньте документацію Mozilla .

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

4

Інші відповіді тут працюють на щось на кшталт Math, який є статичним об'єктом. Але вони не працюють для екземпляра об'єкта, наприклад, дати. Я знайшов, що працює:

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/

Це не спрацює з чимось на зразок оригінального запитання (Math), тому вибирайте рішення, виходячи з ваших потреб. Я публікую це тут, тому що Google надіслав мені це питання, але я хотів знати, як це зробити для екземплярів об'єктів.


3

Коротка відповідь - це ти не можеш, бо Mathі Date(вгорі голови, я впевнений, що є й інші) не є нормальними предметами. Щоб побачити це, створіть простий тестовий сценарій:

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

Ви бачите, що він представляє як об'єкт так само, як це робить документ у цілому, але коли ви насправді намагаєтеся бачити цей об’єкт, ви бачите, що це власний код і щось не піддається тому ж переліченню.


1

Mathмає статичний метод, куди ви можете дзвонити прямо як, Math.abs()а Dateв той час як має статичний метод, як, Date.now()а також метод екземпляра, де вам потрібно створити новий екземпляр спочатку var time = new Date()для виклику 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);

Звичайно, вам потрібно буде відфільтрувати отримані ключі для статичного методу, щоб отримати фактичні назви методів, оскільки ви також можете отримати length, name, які не є функцією у списку.

Але як, якщо ми хочемо отримати весь доступний метод з класу, який розширює інший клас?
Звичайно, вам потрібно буде просканувати корінь прототипу, як користуватися __proto__. Для економії часу ви можете використовувати скрипт нижче, щоб отримати статичний метод та екземпляр глибокого методу.

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

Якщо ви хочете отримати методи від створеного екземпляра, не забудьте передати constructorйого.


0

Я вважаю, що існує проста історична причина, чому ви не можете перерахувати, наприклад, такі вбудовані об'єкти, як Array. Ось чому:

Методи - це властивості прототипу-об'єкта, скажімо Object.prototype. Це означає, що всі об'єкти-екземпляри успадкують ці методи. Ось чому ви можете використовувати ці методи на будь-якому об’єкті. Скажімо, наприклад, .toString ().

Тож методів ІЧ було безліч, і я хотів би повторити слово {a: 123} з: "for (введіть {a: 123}) {...}", що буде? Скільки разів ця петля буде виконана?

Був би повторений один раз для одного ключа 'a' у нашому прикладі. АЛЕ один раз для кожного перераховується майна Object.prototype. Отже, якщо методи були переліченими (за замовчуванням), то будь-який цикл над будь-яким об’єктом також би переходив на всі його успадковані методи.


1
так як примітиви зазвичай успадковують від прототипу, це можливо, Object.getOwnPropertyNames(Array.prototype)наприклад
lfender6445

що ви маєте на увазі методи властивості Object.prototype. ? Кожна властивість є властивостями Object.prototype у випадку Object
debugmode
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.