web-dev-qa-db-fra.com

Obtenir les fonctions (méthodes) d'une classe

Je dois extraire dynamiquement les propriétés et les fonctions d'une classe ES6. Est-ce seulement possible?

Avec une boucle for ... in, je ne parviens qu'à parcourir les propriétés d'une instance de classe:

class Foo {
  constructor() {
    this.bar = "hi";
  }
  someFunc() {
    console.log(this.bar);
  }
}
var foo = new Foo();
for (var idx in foo) {
  console.log(idx);
}

Sortie:

bar
59
Jan

Cette fonction aura toutes les fonctions. Hérité ou non, énumérable ou non. Toutes les fonctions sont incluses.

function getAllFuncs(obj) {
    var props = [];

    do {
        props = props.concat(Object.getOwnPropertyNames(obj));
    } while (obj = Object.getPrototypeOf(obj));

    return props.sort().filter(function(e, i, arr) { 
       if (e!=arr[i+1] && typeof obj[e] == 'function') return true;
    });
}

Faire le test

getAllFuncs([1,3]);

sortie de la console:

["constructor", "toString", "toLocaleString", "join", "pop", "Push", "concat", "reverse", "shift", "unshift", "slice", "splice", "sort", "filter", "forEach", "some", "every", "map", "indexOf", "lastIndexOf", "reduce", "reduceRight", "entries", "keys", "constructor", "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "__defineGetter__", "__lookupGetter__", "__defineSetter__", "__lookupSetter__"]

Note

Il ne renvoie pas les fonctions définies via des symboles.

39
Muhammad Umer

Les membres d'une classe sont non énumérables . Pour les obtenir, vous devez utiliser Object.getOwnPropertyNames :

var propertyNames = Object.getOwnPropertyNames(Object.getPrototypeOf(foo));
// or
var propertyNames = Object.getOwnPropertyNames(Foo.prototype);

Bien sûr, cela n'obtiendra pas les méthodes héritées. Aucune méthode ne peut vous donner à tous. Vous devez traverser la chaîne de prototypes et obtenir les propriétés de chaque prototype individuellement.

41
Felix Kling

ES6 ajoute Reflection, ce qui rend le code un peu plus propre.

function getAllMethodNames(obj) {
  let methods = new Set();
  while (obj = Reflect.getPrototypeOf(obj)) {
    let keys = Reflect.ownKeys(obj)
    keys.forEach((k) => methods.add(k));
  }
  return methods;
}


/// a simple class hierarchy to test getAllMethodNames


// kind of like an abstract base class
class Shape {
  constructor() {}
  area() {
    throw new Error("can't define area for generic shape, use a subclass")
  }
}

// Square: a shape with a sideLength property, an area function and getSideLength function
class Square extends Shape {
  constructor(sideLength) {
    super();
    this.sideLength = sideLength;
  }
  area() {
    return this.sideLength * this.sideLength
  };
  getSideLength() {
    return this.sideLength
  };
}

// ColoredSquare: a square with a color
class ColoredSquare extends Square {
  constructor(sideLength, color) {
    super(sideLength);
    this.color = color;
  }
  getColor() {
    return this.color
  }
}


let temp = new ColoredSquare(2, "red");
let methods = getAllMethodNames(temp);
console.log([...methods]);
24
skav

Il y avait quelques problèmes dans la réponse de @MuhammadUmer pour moi (symboles, index i+1, liste des méthodes Object, etc ...), je me suis donc inspiré de cela pour

(avertissement TypeScript compilé pour ES6)

const getAllMethods = (obj) => {
    let props = []

    do {
        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)
    }
    while (
        (obj = Object.getPrototypeOf(obj)) &&   //walk-up the prototype chain
        Object.getPrototypeOf(obj)              //not the the Object prototype methods (hasOwnProperty, etc...)
    )

    return props
}

Cette fonction listera toutes les méthodes d'une instance de la classe, y compris celles héritées, mais le constructeur et celles du prototype Object.

Tester

La fonction retourne

[ 'asyncMethod',
  'echo',
  'generatorMethod',
  'ping',
  'pong',
  'anotherEcho' ]

listant les méthodes d'une instance de TestClass (TypeScript)

class Echo  {
    echo(data: string): string {
        return data
    }
    anotherEcho(data: string): string {
        return `Echo ${data}`
    }
}


class TestClass extends Echo {

    ping(data: string): string {
        if (data === 'ping') {
            return 'pong'
        }
        throw new Error('"ping" was expected !')
    }

    pong(data: string): string {
        if (data === 'pong') {
            return 'ping'
        }
        throw new Error('"pong" was expected !')
    }

    //overridden echo
    echo(data: string): string {
        return 'blah'
    }

    async asyncMethod(): Promise<string> {
        return new Promise<string>((resolve: (value?: string) => void, reject: (reason?: any) => void) => {
            resolve('blah')
        })
    }

    * generatorMethod(): IterableIterator<string> {
        yield 'blah'
    }
}
8
Bruno Grieder

Pour rendre les membres de la classe énumérable, vous pouvez utiliser Symbol.iterator

Je devais obtenir toutes les méthodes d'objet autorisées (y compris héritées). J'ai donc créé la classe "Enumerable" et toutes mes classes de base héritées de lui.

class Enumerable {
  constructor() {

    // Add this for enumerate ES6 class-methods
    var obj = this;

    var getProps = function* (object) {
      if (object !== Object.prototype) {
        for (let name of Object.getOwnPropertyNames(object)) {
          let method = object[name];
          // Supposedly you'd like to skip constructor and private methods (start with _ )
          if (method instanceof Function && name !== 'constructor' && name[0] !== '_')
            yield name;
        }
        yield* getProps(Object.getPrototypeOf(object));
      }
    }

    this[Symbol.iterator] = function*() {
      yield* getProps(obj);
    }
    // --------------
  }
}
2
Santinell