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

249

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

 alert(show_all_methods(Math));

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

abs, acos, asin, atan, atan2, ceil, cos, exp, floor, log, max, min, pow, random,round, sin, sqrt, tan, 
GeekTantra
источник

Ответы:

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

Из ECMA-262 Edition 3 :

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

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

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

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


Обновление: один из пользователей SO, CMS, сообщил{ DontEnum } мне об ошибке IE .

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

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

Энди Э
источник
Энди Э, спасибо за указание на это. Очевидно, я не знал об этом, и я ценю ваши усилия, чтобы выкопать это и упомянуть об этом здесь. Еще раз спасибо :)
Roland Bouman
@ Роланд: Не беспокойся. Может быть, это немного грустно, но у меня есть спецификация, хранящаяся в моей папке «Документы», так что на самом деле не нужно много копать!
Энди Э,
Разве нет способа получить список всех методов в более новых реализациях JS, тогда? Как Node.js и V8? Как мы работаем с объектами отражения и самоанализа, как мы это делали, например, для фальшивых фреймворков и т. Д.? Я думал, что просто забыл JS, но я думаю, что все изменилось за эти годы :)
d11wtq
2
@ d11wtq, с реализациями ES5 вы можете вызывать Object.getOwnPropertyNames(), что будет возвращать даже не перечисляемые свойства и методы.
Энди E
поскольку все объекты наследуются от своего прототипа, не лучше ли сделать что-то подобное 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 - это билет. Это даже работает в Nashorn. Они просто изменили имена методов объекта 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я получаю больше удачи. Честно говоря, это немного неожиданно, я не знаю, почему это не работает для математики и т. Д.
Ролан Боуман
4
@Roland: Это потому, что documentи windowявляются объектами с перечисляемыми свойствами, предоставляемыми браузером, они не являются частью среды выполнения сценариев. Нативные объекты есть и, очевидно, свойства не перечисляются.
Энди Э,
1
Любая E, я не согласен, это очевидно. Я имею в виду, это очевидно, поскольку мы не можем перечислить их. Но я не вижу логики относительно того, почему эти встроенные модули должны предотвращать перечисление их свойств. Просто любопытно, есть ли какая-то часть стандарта, в которой говорится, что эти встроенные модули не должны иметь перечисляемых свойств?
Роланд Боуман
@Roland: извини, я имел в виду, что очевидно, что они не перечислимы, так как они не появляются с for-in. Смотрите мой ответ ниже для цитаты из спецификации.
Энди Э,
@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его.

StefansArya
источник
0

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

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

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

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

Panu Logic
источник
1
поскольку примитивы обычно наследуются от прототипа, это возможно, Object.getOwnPropertyNames(Array.prototype)например,
lfender6445,
что вы имеете в виду, методы являются свойствами Object.prototype. ? Каждое свойство является свойствами Object.prototype в случае Object
debugmode