Как отобразить все методы объекта?

Я хочу знать, как перечислить все методы, доступные для объекта, например:

 alert(show_all_methods(Math));

Это должно напечатать:

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

11 ответов

Решение

Ты можешь использовать 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 не являются встроенными, они определены браузером и, скорее всего, перечислены по дизайну.

Из ECMA-262 Edition 3:

Глобальный объект
Существует уникальный глобальный объект (15.1), который создается до того, как элемент управления войдет в любой контекст выполнения. Первоначально глобальный объект имеет следующие свойства:

• Встроенные объекты, такие как Math, String, Date, parseInt и т. Д. Они имеют атрибуты {DontEnum}.
• Дополнительные свойства, определенные хостом. Это может включать свойство, значением которого является сам глобальный объект; например, в объектной модели документа HTML свойством окна глобального объекта является сам глобальный объект.

Когда элемент управления входит в контексты выполнения и выполняется код ECMAScript, к глобальному объекту могут быть добавлены дополнительные свойства, а начальные свойства могут быть изменены.

Следует отметить, что это означает, что эти объекты не являются перечисляемыми свойствами объекта Global. Если вы посмотрите остальную часть документа спецификации, вы увидите, что большинство встроенных свойств и методов этих объектов имеют { DontEnum } Атрибут установлен на них.


Обновление: такой же пользователь SO, CMS, привел ошибку IE относительно { DontEnum } моему вниманию

Вместо проверки атрибута DontEnum, [Microsoft] JScript пропустит любое свойство в любом объекте, где есть свойство с тем же именем в цепочке прототипов объекта, которая имеет атрибут DontEnum.

Короче, будьте осторожны при именовании свойств вашего объекта. Если есть встроенное свойство или метод-прототип с таким же именем, то IE пропустит его при использовании for...in петля.

С 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"] ни в каком конкретном порядке.

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

Таким образом, вы получите все методы, которые вы можете вызвать obj, Это включает методы, которые он "наследует" от своего прототипа (например, getMethods() в Яве). Если вы хотите видеть только те методы, которые определены непосредственно obj вы можете проверить с hasOwnProperty:

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

Самая современная поддержка браузеров console.dir(obj), который вернет все свойства объекта, которые он унаследовал через своего конструктора. См. Документацию Mozilla для получения дополнительной информации и текущей поддержки браузера.

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

Другие ответы здесь работают для чего-то вроде 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 отправил мне этот вопрос, но я хотел знать, как это сделать для экземпляров объектов.

Подход, который хорошо работает с классами ES6

Это, вероятно, то, что имеет в виду большинство новичков в ES6, когда они ищут «как составить список методов объекта».

Это было адаптировано из: /questions/16204170/es6-perebirat-metodyi-klassa/16204185#16204185

      const isGetter = (x, name) => (Object.getOwnPropertyDescriptor(x, name) || {}).get
const isFunction = (x, name) => typeof x[name] === "function";
const deepFunctions = x =>
  x && x !== Object.prototype &&
  Object.getOwnPropertyNames(x)
    .filter(name => isGetter(x, name) || isFunction(x, name))
    .concat(deepFunctions(Object.getPrototypeOf(x)) || []);
const distinctDeepFunctions = x => Array.from(new Set(deepFunctions(x)));
const getMethods = (obj) => distinctDeepFunctions(obj).filter(
    name => name !== "constructor" && !~name.indexOf("__"));

// Example usage.

class BaseClass {
  override() { }
  baseMethod() { }
}

class DerivedClass extends BaseClass {
  override() { }
  get myGetter() { }
  static myStatic() { }
}

const obj = new DerivedClass();
const methods = getMethods(obj)
methods.sort()
const assert = require('assert')
assert(methods[0] === 'baseMethod')
assert(methods[1] === 'myGetter')
assert(methods[2] === 'override')

console.log(getMethods(Math))

Обратите внимание, как он также включает методы базовых классов, поскольку большинство пользователей захотят увидеть, какие методы они могут вызывать для объекта.

Также кажется, что он работает с Math, он выводит:

      [
  'abs',    'acos',  'acosh',  'asin',
  'asinh',  'atan',  'atanh',  'atan2',
  'ceil',   'cbrt',  'expm1',  'clz32',
  'cos',    'cosh',  'exp',    'floor',
  'fround', 'hypot', 'imul',   'log',
  'log1p',  'log2',  'log10',  'max',
  'min',    'pow',   'random', 'round',
  'sign',   'sin',   'sinh',   'sqrt',
  'tan',    'tanh',  'trunc'
]

Проверено на Node.js 14.17.0.

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 этого.

Короткий ответ: ты не можешь, потому что 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>

Вы видите, что он представлен как объект точно так же, как документ в целом, но когда вы на самом деле пытаетесь увидеть этот объект, вы видите, что это нативный код и что-то, что не перечислено для перечисления таким же образом.

Я полагаю, что существует простая историческая причина, по которой вы не можете перечислить методы встроенных объектов, таких как, например, Array. Вот почему:

Методы - это свойства объекта-прототипа, скажем, Object.prototype. Это означает, что все экземпляры Object будут наследовать эти методы. Вот почему вы можете использовать эти методы на любом объекте. Скажите.toString(), например.

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

В нашем примере это будет повторено один раз для единственного ключа "а". НО ТАКЖЕ один раз для каждого перечисляемого свойства Object.prototype. Таким образом, если бы методы были перечисляемыми (по умолчанию), то любой цикл по любому объекту также должен был бы пройти по всем его унаследованным методам.

Вдохновленный другими ответами, вот рекурсивный способ поиска всех методов:

      // Find all methods of an object, up to the root prototype
function findAllMethods(obj, methods = []) {
  if (!obj) {
    return [...new Set(methods)];
  }
  
  const props = Object.getOwnPropertyNames(obj);
  return findAllMethods(Object.getPrototypeOf(obj), [
    ...methods,
    ...props.filter(prop => typeof obj[prop] === 'function'),
  ]);
}

Если вам нужны все методы, включая унаследованные:

Другие вопросы по тегам