【问题标题】: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, …

【问题讨论】:

    标签: javascript object methods


    【解决方案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://stackoverflow.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
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-12-06
            • 2019-06-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多