2009-09-23 11 views
0

je l'ai écrit la fonction suivante qui permet la création de classes singleton de cours donnés:Création d'un singleton de toute classe donnée en javascript

function SingletonFrom(Constructor) { 
    return function() { 
     var self = arguments.callee; 
     if (self._instance === undefined) { 
      switch (arguments.length) { // this is ugly 
       case 0: self._instance = new Constructor(); break; 
       case 1: self._instance = new Constructor(arguments[0]); break; 
       case 2: self._instance = new Constructor(arguments[0], arguments[1]); break; 
       // [...] 
       case 10: // ... 
       default: throw new Error('Error in Singleton: Constructor may take at most 10 arguments'); break; 
      } 
     } 
     return self._instance; 
    } 
} 

Exemple:

var Array_Singleton = new SingletonFrom(Array); 
var x = new Array_Singleton(1,2,3); // [1,2,3] 
var y = new Array_Singleton(0,0,0,0); // [1,2,3] 
alert(x === y); // true 

Il fonctionne très bien, mais je Je ne suis pas très content de l'instruction switch. Le problème est de passer un nombre variable d'arguments à un constructeur appelé avec le mot-clé "new" n'est pas possible. Donc, ma fonction Constructeur ne doit pas prendre plus de 10 arguments. Par exemple, cela échouera:

new Array_Singleton(1,2,3,4,5,6,7,8,9,10,11); 

Un moyen de contourner ce problème?

+0

x == y résultant vrai?Pourquoi voudriez-vous que dans votre exemple ci-dessus ?? Cela me semble extrêmement dangereux. – Juri

+0

Dans certaines situations, cela pourrait être utile. Premier arrivé, premier servi ... Dans mon projet actuel, mes constructeurs Obj_Singleton sont tous appelés avec les mêmes arguments de toute façon, ce qui rend les choses plus faciles. Naturellement, vous pouvez modifier la méthode pour signaler une erreur si une instance existe. – user123444555621

Répondre

0

Sur le dessus de ma tête, je ne peux penser à un et je ne le recommanderais probablement pas sans un quantité de tests extrêmement lourde.

Cela étant dit; Au lieu de transmettre vos paramètres séparément, faites-les passer en tableau. Vous pouvez ensuite parcourir le tableau et créer une chaîne avec votre appel à new. Une fois que vous avez votre chaîne, vous pouvez appeler eval() pour exécuter votre commande générée.

Cela vous donnera un moyen de gérer n'importe quel nombre dynamique de paramètres.

1

Javascript a cette limitation gênante de ne pas pouvoir appeler constructeur avec un tableau comme une liste d'arguments. Ce qui est généralement accompli avec Function.prototype.apply lors de l'appel de la fonction en tant que fonction (par exemple foo(...)), ne peut pas être facilement appliqué à une fonction lorsqu'elle est appelée en tant que constructeur (par exemple new foo(...)).

Je suppose que c'est exactement pourquoi vous recourez à switch là.

function foo(a,b) { 
    return a+b; 
} 
foo.apply(null, [1,2]); // 3 

Mais pas si facile avec le constructeur:

function Person(fname, lname) { 
    this.fname = fname; 
    this.lname = lname; 
} 
Person.prototype.speak = function() { 
    return 'Hi. My name is: ' + this.fname + ' ' + this.lname; 
} 

new Person.apply(null, ['John', 'Appleseed']); // doesn't work! 

Pour contourner cela, vous pouvez créer une aide simple qui serait pratiquement simuler ce new-t, mais cette fois avec apply. L'algorithme est simple:

  1. Créez un objet vide avec une chaîne de prototypes appropriée, sans appeler le constructeur.
  2. Utilisez apply pour appeler le constructeur dans le contexte de cet objet nouvellement créé, en lui passant la liste d'arguments avec apply.

Il ressemblerait à quelque chose comme ceci:

function newApply(ctr, array) { 
    function F(){} 
    F.prototype = ctr.prototype; 
    var obj = new F(); 
    ctr.apply(obj, array); 
    return obj; 
} 
newApply(Person, ['John', 'Appleseed']); // returns new object 

Sinon, vous pouvez éviter F création d'un objet lors de l'exécution faire des économies sur la consommation de la performance et de la mémoire:

var newApply = (function(){ 
    function F(){} 
    return function(ctr, array) { 
    F.prototype = ctr.prototype; 
    var obj = new F(); 
    ctr.apply(obj, array); 
    return obj; 
    } 
})(); 
+0

L'avez-vous eu ici? http://stackoverflow.com/questions/813383/how-can-i-construct-an-object-using-an-array-of-values-for-parameters-rather-tha Indépendamment, les mêmes mises en garde que celles mentionnées plus haut il s'applique ici (sans jeu de mots;): cela ne fonctionne pas avec * aucun * des built-ins, dans n'importe quel navigateur. Par exemple 'newApply (Date, [2009,5,5])' échoue, 'newApply (Array, [5,5,5])' échoue, etc etc –

+0

IOWs, vous devriez mentionner cette mise en garde pour faire un bon autrement répondre encore mieux. –

+0

@crescentfresh Oups, j'aurais dû chercher 'newApply' au lieu de répéter ce qui était déjà dit :) Je l'utilise depuis quelques années mais jamais avec des constructeurs natifs. C'est un bon point que vous apportez. Maintenant que je le regarde, il est logique que 'newApply' ne fonctionne pas avec, disons,' Date', puisque 'Date' ne peut pas être" sous-classé "avec succès (l'objet dérivé aura le [[Prototype]] mais son [[Class]] serait "Object", pas une "Date"). – kangax