如何显示对象的所有方法?


249

我想知道如何列出对象可用的所有方法,例如:

 alert(show_all_methods(Math));

这应该打印:

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

Answers:


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及更低版本)中,内置对象的属性不可枚举。像windowdocument不是内置的对象,它们是由浏览器定义的,很可能是通过设计枚举的。

ECMA-262第3版开始

全局对象
有一个唯一的全局对象(15.1),它在控制进入任何执行上下文之前创建。最初,全局对象具有以下属性:

•内置对象,例如Math,String,Date,parseInt等。这些对象具有属性{DontEnum}
•其他主机定义的属性。这可能包括一个属性,其值为全局对象本身。例如,在HTML文档对象模型中,全局对象的window属性是全局对象本身。

当控件进入执行上下文时,以及在执行ECMAScript代码时,可以将其他属性添加到全局对象,并且可以更改初始属性。

我应该指出,这意味着这些对象不是Global对象的不可枚举的属性。如果翻阅规范文档的其余部分,您将看到这些对象的大多数内置属性和方法{ DontEnum }在其上设置了属性。


更新:一位SO用户CMS带来了一个与我有关{ DontEnum }IE错误

[Microsoft] JScript不会检查DontEnum属性,而是跳过对象原型链中具有属性DontEnum的同名属性的任何对象中的任何属性。

简而言之,在命名对象属性时要当心。如果存在具有相同名称的内置原型属性或方法,则IE在使用for...in循环时将跳过它。


Andy E,感谢您指出这一点。显然,我没有意识到这一点,我感谢您为挖掘出来并在此处提及而付出的努力。再次感谢:)
罗兰·鲍曼

@Roland:不用担心。也许有点难过,但是我将规范存储在我的Documents文件夹中,因此实际上并不需要太多的挖掘工作!
安迪E

那么,没有办法获取较新的JS实现中所有方法的列表吗?像Node.js和V8一样?我们如何像过去那样进行反射和内省对象,例如模拟对象框架等?我以为我只是忘记了JS,但多年来我想事情已经发生了变化:)
d11wtq 2012年

2
@ d11wtq,在ES5实现中,您可以调用Object.getOwnPropertyNames(),这甚至会返回不可枚举的属性和方法。
Andy E

由于所有对象都继承自其原型,所以做类似的事情会更好 Object.getOwnPropertyNames(Array.prototype) 吗?
lfender6445 '16

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,因此了解这些东西是很不错的。
Andy E 2010年

1
@Andy-微软非常重视IE9,这让我很高兴:)
Anurag 2010年

console.log(function(a){return Object.getOwnPropertyNames(a).filter(function(b){return“ function” == typeof a [b]})}(Math)); 谢谢!
2012年

1
getOwnPropertyNames是票证。它甚至可以在Nashorn中使用。他们只是更改了Java对象方法的名称,而我能够通过运行Object.getOwnPropertyNames(Java)
cayhorstmann,2013年

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(","));

是的,我也注意到这一点。当我使用类似的东西documentwindow获得更多的运气时。坦率地说这是一个有点意外,我不知道为什么它不会为数学等方面的工作
罗兰·布曼

4
@Roland:这是因为documentwindow是浏览器提供的具有可枚举属性的对象,它们不是脚本运行时的一部分。显然,本机对象是不可枚举的。
Andy E 2010年

1
任何E,我都不同意。我的意思是,显而易见,因为我们似乎无法枚举它们。但是我看不出为什么这些内置程序应该阻止枚举其属性的逻辑。只是好奇,该标准中是否有某些部分规定这些内置程序不应具有可枚举的属性?
罗兰·鲍曼

@Roland:对不起,我的意思是很明显,它们并不是无法枚举的,因为它们不会出现for-in。有关规格的报价,请参见下面的回答。
Andy E 2010年

@Mic:Math是一个内置对象,其属性不可枚举。
Andy E 2010年

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/

不适用于原始问题(数学)之类的东西,因此请根据需要选择解决方案。我将其发布在此处是因为Google向我发送了此问题,但我想知道如何针对对象实例执行此操作。


3

简短的答案是您不能这样做,因为MathDate(在我的头顶上,我确定还有其他)不是正常的对象。要看到这一点,请创建一个简单的测试脚本:

<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()。

因此IF方法是可枚举的,我将用{for(key in {a:123}){...}“遍历说{a:123}会发生什么?该循环执行多少次?

在我们的示例中,将对单个键“ a”进行一次迭代。但对于Object.prototype的每个可枚举属性也要一次。因此,如果方法是可枚举的(默认情况下),那么任何对象上的任何循环都会遍历其所有继承的方法。


1
由于基元通常继承自原型,因此这是可能的Object.getOwnPropertyNames(Array.prototype),例如
lfender6445 '16

您的意思是方法是Object.prototype的属性。?每个属性是Object.prototype中的属性在对象的情况下
debugmode
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.