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

Posted

技术标签:

【中文标题】如何显示一个对象的所有方法?【英文标题】:How to display all methods of an object? 【发布时间】:2011-01-16 12:16:12 【问题描述】:

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

 alert(show_all_methods(Math));

这应该打印出来:

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

【问题讨论】:

【参考方案1】:
var methods = [];
for (var m in obj) 
    if (typeof obj[m] == "function") 
        methods.push(m);
    

alert(methods.join(","));

这样,您将获得可以在obj 上调用的所有方法。这包括它从其原型“继承”的方法(如 java 中的 getMethods())。如果您只想查看obj 直接定义的那些方法,您可以查看hasOwnProperty

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

alert(methods.join(","));

【讨论】:

是的,我也注意到了这一点。当我使用documentwindow 之类的东西时,我会得到更多的运气。坦率地说,这有点出乎意料,我不知道为什么它不适用于数学等。 @Roland:这是因为documentwindow 是浏览器提供的具有可枚举属性的对象,它们不是脚本运行时的一部分。原生对象显然是不可枚举的。 任何 E,我不同意这是显而易见的。我的意思是,这很明显,因为我们似乎无法列举它们。但是我不明白为什么这些内置函数应该阻止枚举它们的属性的逻辑。只是好奇,标准中是否有部分规定这些内置函数不应具有可枚举的属性? @Roland:抱歉,我的意思是很明显它们是不可枚举的,因为它们没有出现 for-in。请参阅下面我的回答以获取规范的引用。 @Mic:Math 是一个内置对象,其属性不可枚举。【参考方案2】:

简短的回答是你不能,因为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>

您会看到它以与 document 整体相同的方式呈现为一个对象,但是当您实际尝试查看该对象时,您会发现它是本机代码,并且某些东西以不同的方式公开用于枚举。

【讨论】:

【参考方案3】:

您可以使用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 Edition 3:

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

• 内置 对象,例如数学、字符串、日期、 parseInt 等 这些都有属性 DontEnum 。 • 定义了额外的主机 特性。这可能包括一个 值为全局的属性 对象本身;例如,在 HTML 文档对象模型窗口 全局对象的属性是 全局对象本身。

作为控制 进入执行上下文,并且作为 执行 ECMAScript 代码, 可以添加其他属性 全局对象和初始 属性可能会改变。

我应该指出,这意味着这些对象不是 Global 对象的可枚举属性。如果您查看规范文档的其余部分,您会发现这些对象的大部分内置属性和方法都设置了 DontEnum 属性。


更新:一位 SO 用户 CMS 引起了我的注意,IE bug regarding DontEnum

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

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

【讨论】:

安迪 E,感谢您指出这一点。显然我没有意识到这一点,我感谢您努力挖掘并在这里提及它。再次感谢:) @Roland:不用担心。也许有点难过,但我的文档文件夹中存储了规范,所以真的不需要太多挖掘! 那么,有没有办法在较新的 JS 实现中获取所有方法的列表呢?喜欢 Node.js 和 V8?我们如何像以前一样进行反射和内省对象,例如模拟对象框架等?我以为我只是忘记了 JS,但我想这些年来事情已经发生了变化 :) @d11wtq,使用 ES5 实现,您可以调用 Object.getOwnPropertyNames(),它甚至会返回不可枚举的属性和方法。 既然所有对象都继承自它们的原型,那么做Object.getOwnPropertyNames(Array.prototype) 之类的东西不是更好吗?【参考方案4】:

大多数现代浏览器都支持console.dir(obj),它将返回通过其构造函数继承的对象的所有属性。有关更多信息和当前浏览器支持,请参阅 Mozilla 的 documentation。

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

【讨论】:

【参考方案5】:

这在 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 - 微软非常重视 IE9,这让我很高兴 :) console.log(function(a)return Object.getOwnPropertyNames(a).filter(function(b)return"function"==typeof a[b])(数学)) ;谢谢! getOwnPropertyNames 是票。它甚至适用于 Nashorn。他们只是更改了 Java 对象的方法名称,我能够通过运行 Object.getOwnPropertyNames(Java) 找出新名称【参考方案6】:

我相信你不能列举一个简单的历史原因 例如 Array 等内置对象的方法。原因如下:

方法是原型对象的属性,比如 Object.prototype。 这意味着所有对象实例都将继承这些方法。那是 为什么你可以在任何对象上使用这些方法。例如说 .toString()。

所以 IF 方法是可枚举的,我会重复说 a:123 with: "for (key in a:123) ..." 会发生什么?多少次 会执行那个循环吗?

在我们的示例中,它将为单个键“a”迭代一次。但是也 Object.prototype 的每个 enumerable 属性一次。因此,如果 方法是可枚举的(默认情况下),然后任何对象上的任何循环都会循环 也包括所有继承的方法。

【讨论】:

因为原语通常从原型继承,这可能是Object.getOwnPropertyNames(Array.prototype) 例如 你的意思是方法是 Object.prototype 的属性。 ?在 Object 的情况下,每个属性都是 Object.prototype 的属性【参考方案7】:

这里的其他答案适用于数学之类的东西,它是一个静态对象。但它们不适用于对象的实例,例如日期。我发现以下工作:

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 向我发送了此问题,但我想知道如何为对象实例执行此操作。

【讨论】:

【参考方案8】:

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

但是如果我们想从扩展另一个类的类中获取所有可用的方法呢? 当然,您需要像使用__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

【讨论】:

【参考方案9】:

适用于 ES6 类和继承的方法

这可能是大多数像我这样的新手 ES6 用户在寻找“如何列出对象方法”时的意思。

改编自:https://***.com/a/47714550/895245

// Define getMethods.
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 上测试。

【讨论】:

非常感谢。出于兴趣,怎么了:!~name ? @user48956 我不知道,但wsvincent.com/javascript-tilde 解释说,它是二进制否定并将-1 转换为0。 是的——但是使用二进制否定有点不寻常,除非你正在处理数字的位操作(对于所有其他情况,通常使用布尔否定:!) 或者换一种说法,为什么不是-1!==name.indexOf(""),或者-1===name.indexOf("") @user48956 性能适合初学者,真正的专业人士只关心code golfing

以上是关于如何显示一个对象的所有方法?的主要内容,如果未能解决你的问题,请参考以下文章

在数组列表中按标题显示所有 DVD 对象时遇到问题

如何在 JavaScript 中只显示前两个对象?

如何从本地存储jquery显示数组中的所有对象

如何查找场景中所有的物体啊,包括在Hierarchy中不显示的物体

如何将一个对象数组中的所有项目从Javascript动态显示为HTML?

如何在不使用 setGridLinesVisible() 方法的情况下永久显示 GridPane 对象网格线?