【问题标题】:ES6 Iterate over class methodsES6 遍历类方法
【发布时间】:2021-12-28 21:37:44
【问题描述】:

鉴于这个类;我将如何迭代它包含的方法?

class Animal {
    constructor(type){
        this.animalType = type;
    }
    getAnimalType(){
        console.log('this.animalType: ', this.animalType );
    }
}

let cat = window.cat = new Animal('cat')

我尝试过以下但没有成功:

for (var each in Object.getPrototypeOf(cat) ){
    console.log(each);
}

【问题讨论】:

  • 不应该迭代Animal.prototype 工作吗?据我了解,底层对象架构仍然与“旧”方式相同。
  • Animal.prototype 公开了这些方法。我认为问题在于迭代它们的方式。 for.. in 似乎不起作用。

标签: javascript ecmascript-6


【解决方案1】:

您可以在原型上使用Object.getOwnPropertyNames:

Object.getOwnPropertyNames( Animal.prototype )
// [ 'constructor', 'getAnimalType' ]

【讨论】:

  • 我还没有尝试过 - 但这对继承的东西有用吗?我不确定它是否会......并且只适用于超级类。
  • @seasick 可以,但您还需要使用Object.getPrototypeOf,才能首先获得原型:Object.getOwnPropertyNames( Object.getPrototypeOf( cat ) )
  • 谢谢!!!!这绝对有效,但考虑到这一切的语法......我想知道这是否是做这样的事情的首选方式。我所追求的只是调用类的所有方法,但不想以文字类型的方式进行,所以我想遍历它们。
  • @seasick 哦,如果你想要 Animal 从其他基类继承的方法,你还必须递归地沿着原型链向上走到链中的每个对象 getOwnPropertyNames。我认为没有其他方法可以获取不可枚举的属性
  • 请注意getOwnPropertyNames 不返回继承的方法,如果您的类扩展了另一个,这些方法将不会被包含。
【解决方案2】:

我知道,我知道,但是嘿...

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 userFunctions = x => distinctDeepFunctions( x ).filter( name => name !== "constructor" && !~name.indexOf( "__" ) );


// example usage

class YourObject {   
   hello() { return "uk"; }
   goodbye() { return "eu"; }
}

class MyObject extends YourObject {
   hello() { return "ie"; }
   get when() { return "soon"; } 
}

const obj = new MyObject();
console.log( userFunctions( obj ) ); // [ "hello", "when", "goodbye" ]

【讨论】:

  • 应该是选择的答案,它适用于 ES6 和动态对象以及 ES6 继承。
  • x[ name ] 将执行一个 getter。使用 (Object.getOwnPropertyDescriptor(x, name) || {}).get || 防止这种情况发生typeof x [ 名称 ] == ...
  • @goofballLogic 你应该检查属性是否仍然存在。可以使用delete 命令删除属性。
【解决方案3】:

这有点复杂,但从整个原型链中获取方法。

function getAllMethodNames (obj, depth = Infinity) {
    const methods = new Set()
    while (depth-- && obj) {
        for (const key of Reflect.ownKeys(obj)) {
            methods.add(key)
        }
        obj = Reflect.getPrototypeOf(obj)
    }
    return [...methods]
}

【讨论】:

    【解决方案4】:

    由于 ES6 类上的方法是不可枚举的,因此您别无选择,只能使用 Object.getOwnPropertyNames() 获取其所有属性的数组。

    实现这一点后,有几种方法可以提取方法,其中最简单的可能是使用Array.prototype.forEach()。

    查看以下 sn-p:

    Object.getOwnPropertyNames(Animal.prototype).forEach((value) => {
        console.log(value);
    })
    

    【讨论】:

      【解决方案5】:

      如果您只需要函数(例如替换为_.functions),请尝试这种线性方式

      function getInstanceMethodNames (obj) {
          return Object
              .getOwnPropertyNames (Object.getPrototypeOf (obj))
              .filter(name => (name !== 'constructor' && typeof obj[name] === 'function'));
      }
      

      【讨论】:

        【解决方案6】:

        检查这个小提琴

        https://jsfiddle.net/ponmudi/tqmya6ok/1/

        class Animal {
            constructor(type){
                this.animalType = type;
            }
            getAnimalType(){
                console.log('this.animalType: ', this.animalType );
            }
        }
        
        let cat = new Animal('cat');
        
        //by instance
        document.getElementById('1').innerHTML = Object.getOwnPropertyNames(cat);
        
        //by getting prototype from instance
        document.getElementById('2').innerHTML = Object.getOwnPropertyNames(Object.getPrototypeOf(cat));
        
        //by prototype
        document.getElementById('3').innerHTML = Object.getOwnPropertyNames(Animal.prototype);
        

        【讨论】:

          【解决方案7】:

          这是https://stackoverflow.com/a/35033472/3811640此处建议的答案的增强版

          我默认添加参数 deep, deep= Infinity 来提取包括父函数在内的所有函数。 deep =1 提取给定类的直接方法。

          getAllMethods = function (obj, deep = Infinity) {
              let props = []
          
              while (
                (obj = Object.getPrototypeOf(obj)) && // walk-up the prototype chain
                Object.getPrototypeOf(obj) && // not the the Object prototype methods (hasOwnProperty, etc...)
                deep !== 0
              ) {
                const l = Object.getOwnPropertyNames(obj)
                  .concat(Object.getOwnPropertySymbols(obj).map(s => s.toString()))
                  .sort()
                  .filter(
                    (p, i, arr) =>
                      typeof obj[p] === 'function' && // only the methods
                      p !== 'constructor' && // not the constructor
                      (i == 0 || p !== arr[i - 1]) && // not overriding in this prototype
                      props.indexOf(p) === -1 // not overridden in a child
                  )
                props = props.concat(l)
                deep--
              }
          
              return props
            }
            
          
          class Foo {
            
            method01 (){
            }
            
            method02 (){
            }
            
            }
            
            
            class FooChield extends Foo {
            
            method01(){
            }
            
            method03(){
            }
            } 
            
          
            
            console.log('All methods', getAllMethods(new FooChield())) 
            
            console.log('Direct methods', getAllMethods(new FooChield(),1))
          enter code here
          

          【讨论】:

            【解决方案8】:

            如果您还需要获取超类方法,可以反复调用Object.getPrototypeOf(),直到找到所有方法。当您到达Object.prototype 时,您可能会想停下来,因为那里的方法是基本的,您通常不想使用任何使用反射的代码来接触它们。

            Get functions (methods) of a class 的问题有一个答案,其中包含一个用于执行此操作的函数,但它有一些缺点(包括使用具有修改函数参数的副作用的循环条件,我认为这会产生两个有问题的代码样式一行代码中的选择...),所以我在这里重写了它:

            export function listMethodNames (object, downToClass = Object)
            {
                // based on code by Muhammad Umer, https://stackoverflow.com/a/31055217/441899
                let props = [];
            
                for (let obj = object; obj !== null && obj !== downToClass.prototype; obj = Object.getPrototypeOf(obj))
                {
                    props = props.concat(Object.getOwnPropertyNames(obj));
                }
            
                return props.sort().filter((e, i, arr) => e != arr[i+1] && typeof object[e] == 'function');
            }
            

            除了修复原始代码中的错误(它没有将对象复制到循环的另一个变量中,因此当它用于在返回行中进行过滤时,它不再有效)这给出了一个可选参数,用于在可配置的类处停止迭代。默认为Object(所以Object的方法被排除在外;如果你想包含它们,你可以使用一个没有出现在继承链中的类......也许制作一个标记类,例如@987654327 @ 可能有意义)。我还将do 循环更改为更清晰的for 循环,并将旧式function ... 过滤器函数重写为ES6 箭头函数以使代码更紧凑。

            【讨论】:

              【解决方案9】:

              另一种在不列出构造函数和其他属性的情况下获取它们的方法:

              var getMethods = function(obj) {
                  const o = Reflect.getPrototypeOf(obj);
                  const x = Reflect.getPrototypeOf(o);
                  return Reflect.ownKeys(o).filter(it => Reflect.ownKeys(x).indexOf(it) < 0);
              }
              

              【讨论】:

                【解决方案10】:

                为什么这里的答案如此复杂?让我们变得简单。

                Object.getOwnPropertyNames(...) 是获取非继承类方法名称的标准且唯一的方法。 [按字母顺序作为数组返回]

                Object.getPrototypeOf(...) 是获取继承原型的标准且唯一的方法。 [作为 Class.prototype 返回]

                所以,只是循环

                  Object.getPrototypeOf(Object.getPrototypeOf( 
                    Object.getPrototypeOf( Object.getPrototypeOf(  
                      ...
                        Object.getPrototypeOf( your_object )
                      ...
                    ))
                  ))
                

                直到它的构造函数 === Object

                  // JavaScript
                
                  class A {
                     myWife() {   console.log(4)   }
                  }
                  class B extends A {
                     myParent() {   console.log(5)   }
                  }
                  class C extends B {
                     myBrother() {   console.log(6)   }
                  }
                  class D extends C {
                     mySelf() {   console.log(7)   }
                  }
                  
                  let obj = new D;
                  
                  function classMethodsA(obj) {
                    let res = {};
                    let p = Object.getPrototypeOf(obj)
                    while (p.constructor !== Object) {
                      for(const k of Object.getOwnPropertyNames(p)){
                        if (!(k in res)) res[k] = obj[k];
                      }
                      p = Object.getPrototypeOf(p)
                    }
                    return res;
                  }
                  
                    
                  function classMethodsB(obj) {
                    let res = [];
                    let p = Object.getPrototypeOf(obj);
                    while (p.constructor !== Object) {
                      for(const k of Object.getOwnPropertyNames(p)){
                        if (!res.includes(k)) res.push(k);
                      }
                      p = Object.getPrototypeOf(p);
                    }
                    return res;
                  }
                  
                  
                  document.body.innerHTML=`
                <p>  -- No Constructor -- ${Object.keys(classMethodsA(obj))}</p>
                <p>  -- With Constructor -- ${classMethodsB(obj)}</p>
                  `;

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-08-22
                  • 2021-09-07
                  • 2011-05-23
                  • 1970-01-01
                  • 2016-10-15
                  • 2017-12-07
                  相关资源
                  最近更新 更多