使用原型时从类中获取方法

时间:2019-02-03 22:57:47

标签: javascript

我正在编写一些代码来扩展Object,并且我想遍历对象内的方法。当我输出this.constructor的值时,我看到了我想要的方法(在下面的示例中,我看到了test)。但是,当我使用for遍历方法时,它仅显示方法getType。我该如何获取class A的方法?

Object.prototype.getType = function () {
  // Outputs correct class and methods
  console.log(this.constructor)

  for (let i in this.constructor) {
    // Outputs wrong methods
    console.log(i)
  }
}

class A {
  test() {console.log('hello')}
}

new A().getType()

3 个答案:

答案 0 :(得分:2)

两个问题:

  1. 属性可以枚举– for ...在循环中访问–或不可以。在class中定义方法时,默认值是不可枚举的。 (另一方面,Object.prototype.getType可以枚举,因为这是通过赋值创建属性时的默认值。)

  2. 您正在遍历该对象声称为其构造函数的属性,但是诸如test之类的非静态方法是该构造函数的prototype属性(A.prototype)的属性,而不是构造函数本身(A.test不是东西)。

您可以使用Object.getOwnPropertyNames函数找到不可枚举的属性:

Object.prototype.getType = function () {
  for (let name of Object.getOwnPropertyNames(this.constructor.prototype)) {
    console.log(name)
  }
}

请注意,这仅包括自己的属性 –直接在对象上的属性–因此,如果要包括继承的方法,则必须向上访问原型链。

function* getAllPropertyNames(obj) {
  do {
    yield* Object.getOwnPropertyNames(obj)
  } while (obj = Object.getPrototypeOf(obj))
}

Object.prototype.getType = function () {
  for (let name of getAllPropertyNames(this.constructor.prototype)) {
    console.log(name)
  }
}

如果您想直接在对象上包含属性,则必须从那里开始(并希望this.constructor.prototypeObject.getPrototypeOf(this))。

Object.prototype.getType = function () {
  for (let name of getAllPropertyNames(this)) {
    console.log(name)
  }
}

此外,请勿扩展Object.prototype尤其是没有可枚举的属性。当您执行此操作时,管理层对以神秘方式破坏的依赖关系不承担任何责任。

答案 1 :(得分:1)

也许不是最优雅的解决方案,但是一种实现方法是通过调用Object.getOwnPropertyNames(),并传递对象实例__proto__引用。

Object.prototype.getType = function () {

  /*
  // Outputs correct class and methods
  console.log(this.constructor)

  for (let i in this.constructor) {
    // Outputs wrong methods
    console.log(i)
  }
  */

  for(let j of Object.getOwnPropertyNames(this.__proto__)
  .filter(method => method !== 'constructor')) 
  {
    // Outputs properties of prototype, without constructor method
    console.log(j);
  }
  
}

class A {
  test() {console.log('hello')}
}

new A().getType()

答案 2 :(得分:1)

getType 名称似乎不合适,您要查找的是属性,因此 getAllProperties 似乎是一个更好的名称。

正如其他人所说,出于多种原因,您需要进入[[Prototype]]链:

  1. 对象继承自其构造函数的原型,而不是构造函数本身
  2. 构造函数属性可能未指向实际的构造函数
  3. constructor.prototype 可能不是用于创建对象的那个
  4. 可能有多个构造函数
  5. 继承的属性可能是重复的,您只需要获取“最近”的属性即可。

[[Prototype]]链以 null 结尾,它是Object.prototype的原型,并且是所有原型层次结构的顶部。该函数还应删除每个原型上可能出现的重复项(例如 constructor length 很常见)。

function getAllProperties(obj) {
  // Recursively get properties up [[Prototype]] chain
  function loop(obj) {
    let props = Object.getOwnPropertyNames(obj);
    let proto = Object.getPrototypeOf(obj);
    
    // Stop at null
    if (proto !== null) {
      return props.concat(loop(proto));
    }
    return props;
  }
  
  let allProps = loop(obj);
  
  // Remove duplicates
  return allProps.filter((prop, i) => !allProps.includes(prop, i+1));
}

console.log(getAllProperties('a'))

如果只需要方法,则在进行typeof x == 'function'过滤时使用:

function getAllMethods(obj) {
  // Recursively get properties up [[Prototype]] chain
  function loop(obj) {
    // Get all properties, filter for functions
    let props = Object.getOwnPropertyNames(obj).filter((prop, i, props) => typeof props[prop] == 'function');
    let proto = Object.getPrototypeOf(obj);

    // Stop at null
    if (proto !== null) {
      return props.concat(loop(proto));
    }
    return props;
  }

  let allMethods = loop(obj);

  // Remove duplicates
  return allMethods.filter((prop, i) => !allMethods.includes(prop, i + 1));
}

console.log(getAllMethods('a'))