2017-05-16 1 views
0

Je voudrais créer un décorateur pour stocker une fonction de ce champ dans un tableau pour chaque instance créée d'une classe Je ne peux pas trouver les mots justes pour expliquer donc ici disons que j'ai le code suivant:Javascript Decorators: Comment stocker la fonction avec cette portée?

Je sais que les décorateurs ne sont pas traités pendant l'instanciation de classe.
class Foo 
{ 
    constructor() 
    { 
     this.MyValue = "Foo"; 
    } 

    @StoreFunction() 
    TestA() 
    { 
     console.log("Foo MyValue:", this.MyValue); 
    } 
} 
class Bar extends Foo 
{ 
    constructor() 
    { 
     this.MyValue = "Bar"; 
    } 

    @StoreFunction() 
    TestB() 
    { 
     console.log("Bar MyValue:", this.MyValue); 
    } 
} 

function StoreFunction() 
{ 
    return function(target, key, descriptor) 
    { 
     // How would i go about saving the function there so i can call it later?? 
     return descriptor; 
    } 
} 

J'ai donc essayé de faire ce qui suit dans StoreFunction.

var StoredFunctions = []; 

function StoreFunction() 
{ 
    return function(target, key, descriptor) 
    { 
     if(target._StoredFunctions) 
     { 
      target._StoredFunctions = []; 
     } 
     // Save the function's name 
     target._StoredFunctions.push(key); 

     return descriptor; 
    } 
} 

Puis les lier dans le constructeur.

class Foo 
{ 
    constructor() 
    { 
     this.MyValue = "Foo"; 
     this.BindFunctions() 
    } 

    BindFunctions() 
    { 
     if(this._StoredFunctions) 
     { 
      this._StoredFunctions.forEach(method => { 
       StoredFunctions.push(this[method].bind(this)); 
      }); 
     } 
    } 
} 

Mais encore une fois, this._StoredFunctions ne stocke pas correctement les StoredFunctions car ils sont statiques. Je ne suis pas sûr de savoir comment le faire fonctionner correctement.

S'il vous plaît laissez-moi savoir si la question n'est pas claire que j'ai du mal à expliquer le problème.

+1

Quel est le résultat final que vous souhaitez obtenir? Je suis particulièrement troublé par l'expression «exposer la fonction», car les méthodes ne sont pas cachées sur les objets, elles n'ont donc pas besoin d'être exposées. –

+0

Lorsque vous dites "exposer la fonction" essayez-vous de les placer dans un tableau globalement accessible "à utiliser pour plus tard"? Pourquoi ne pas simplement utiliser l'objet et sa méthode "plus tard"? – evolutionxbox

+0

Je sais qu'ils ne sont pas des objets cachés. Je voudrais les mettre dans un tableau et les lier aux événements de clavier ou à tout autre événement que je déclenche. Alors disons que je crée une fonction avec ce décorateur alors je peux créer une instance de cette classe et appeler toutes ces fonctions pour chaque instance créée. – Azarus

Répondre

1

Vous pouvez insérer une sous-classe automatique qui peut gérer de faire le travail nécessaire de « exposer » (ou autrement faire des choses avec) les fonctions marquées, voir les commentaires:

// `exposable` sets the class up for `exposed` on methods 
function exposable() { 
    return function decorator(target, name) { 
     // Create our subclass with the same name 
     const o = {[name]: class extends target { 
      constructor(...args) { 
       super(...args); 
       // "Expose" the bound methods 
       this.exposed = {}; 
       Object.getOwnPropertyNames(target.prototype).forEach(name => { 
        const method = target.prototype[name]; 
        if (method.exposed) { 
         this.exposed[name] = method.bind(this); 
        } 
       }); 
      } 
     }}; 
     return o[name]; 
    }; 
} 

// `expose` marks a method to be exposed in the constructor 
function exposed(state) { 
    return function decorator(target, name, config) { 
     config.value.exposed = true; 
     return config; 
    }; 
} 

// Example 
@exposable() 
class Foo { 
    constructor(name) { 
     this.name = name; 
    } 

    @exposed() 
    testA() { 
     console.log("testA says " + this.name); 
    } 

    testB() { 
     console.log("testB says " + this.name); 
    } 
} 

const f = new Foo("Fred"); 
f.exposed.testA(); // Says "testA says Fred" because it's bound 

Vous avez dit que nous peut s'appuyer sur une classe de base commune (Foo). Si oui, nous pouvons déplacer la logique en elle-même Foo:

// `expose` marks a method to be exposed in the constructor 
function exposed(state) { 
    return function decorator(target, name, config) { 
     config.value.exposed = true; 
     return config; 
    }; 
} 

// Example 
class Foo { 
    constructor(name) { 
     this.name = name; 
     this.exposed = Object.create(null); 
     let proto = Object.getPrototypeOf(this); 
     while (proto && proto !== Object.prototype) { 
      Object.getOwnPropertyNames(proto).forEach(name => { 
       if (!this.exposed[name]) { 
        const method = this[name]; 
        if (typeof method === "function" && method.exposed) { 
         // Expose it 
         this.exposed[name] = method.bind(this); 
        } 
       } 
      }); 
      proto = Object.getPrototypeOf(proto); 
     } 
    } 

    @exposed() 
    testA() { 
     console.log("testA says " + this.name); 
    } 
} 

class Bar extends Foo { 

    @exposed() 
    testB() { 
     console.log("testB says " + this.name); 
    } 
} 

const f = new Foo("Fred"); 
f.exposed.testA(); // Says "testA says Fred" because it's bound 
const b = new Bar("Barney"); 
b.exposed.testA(); // Says "testA says Barney" because it's bound 
b.exposed.testB(); // Says "testB says Barney" because it's bound 
+0

Existe-t-il un moyen de laisser le décorateur @exposable() de la classe donc je n'ai qu'à mettre le décorateur sur la fonction? – Azarus

+0

@Azarus: Je ne peux pas y penser immédiatement à moins que vous ne décortiez 'constructor' à la place, ce qui est fondamentalement la même chose. (À moins, bien sûr, que vous ayez besoin de coder manuellement un appel "setup" dans chaque constructeur.) –

+0

Hmm et si je crée une fonction dans la classe parente qui passe par-dessus chaque méthode de classe et regarde si la fonction a été .exposée propriété? Puis lier les fonctions en conséquence? – Azarus

0

T.J. Crowder fourni une réponse correcte, je fournis une solution propre et lisible que je suis venu avec basé sur T.J. Crowder.

var StoredFunctions = {}; 
class Foo 
{ 
    constructor() 
    { 
     this.MyValue = "Foo"; 
     this.SetupStore(); 
    } 

    SetupStore() 
    { 
     let Methods = Object.getOwnPropertyNames(Object.getPrototypeOf(this)); 
     for(let Method of Methods) 
     { 
      if(typeof(this[Method]) == "function" && this[Method]._StoredFunction) 
      { 
       StoredFunctions[Method] = this[Method].bind(this); 
      } 

     } 
    } 

    @StoreFunction() 
    TestA() 
    { 
     console.log("Foo MyValue:", this.MyValue); 
    } 
} 

class Bar extends Foo 
{ 
    constructor() 
    { 
     this.MyValue = "Bar"; 
    } 

    @StoreFunction() 
    TestB() 
    { 
     console.log("Bar MyValue:", this.MyValue); 
    } 
} 



function StoreFunction(StoreInfo) 
{ 
    return function(target, key, descriptor) 
    { 
     descriptor.value._StoredFunction = true; // Mark the function to be stored on initialization 
     return descriptor; 
    } 
}