2010-04-19 4 views
9

J'ai un objet singleton qui utilisent un autre objet (non singleton), d'exiger quelques informations au serveur:JavaScript « classe » et les problèmes singleton

var singleton = (function(){ 

    /*_private properties*/ 
    var myRequestManager = new RequestManager(params, 
    //callbacks 
    function(){ 
     previewRender(response); 
    }, 
    function(){ 
     previewError(); 
    } 
); 

    /*_public methods*/ 
    return{ 

    /*make a request*/ 
    previewRequest: function(request){ 
     myRequestManager.require(request); //err:myRequestManager.require is not a func 
    }, 

    previewRender: function(response){ 
     //do something 
    }, 

    previewError: function(){ 
     //manage error 
    } 
    }; 
}()); 

Ceci est la « classe » qui font la demande au serveur

function RequestManager(params, success, error){ 
    //create an ajax manager 
    this.param = params; 
    this._success = success; //callbacks 
    this._error = error; 
} 

RequestManager.prototype = { 

    require: function(text){ 
    //make an ajax request 
    }, 
    otherFunc: function(){ 
    //do other things 
    } 

}

Le problème est que je ne peux pas appeler myRequestManager.require à l'intérieur objet singleton. Firebug consolle dit: "myRequestManager.require n'est pas une fonction", mais je ne comprends pas où est le problème. Existe-t-il une meilleure solution pour mettre en œuvre cette situation?

+0

Je n'ai pas reçu l'erreur ... D'où viennent les 'params' lorsque vous construisez le RequestManager? –

+0

Dans l'exemple de code que vous donnez, params n'est défini nulle part. Avez-vous laissé quelque chose hors de votre exemple? – Robusto

Répondre

6

Votre code est dans l'ordre que vous l'avez cité, n'est-ce pas? Le singleton apparaît au-dessus de RequestManager dans la source?

Si oui, c'est votre problème. (!) Il est assez subtile, mais en supposant que vos deux bits de code cité sont dans l'ordre que vous avez montré, voici l'ordre dans lequel les choses se passent (je vous expliquerai plus loin):

  1. La fonction RequestManager est défini.
  2. Votre fonction anonyme qui crée votre singleton s'exécute, y compris l'instanciation d'une instance de RequestManager. Le prototype RequestManager est remplacé par un nouveau prototype.

Depuis l'instance myRequestManager a été instancié avant le prototype a été changé, il ne possède pas les fonctions que vous avez définies sur ce (nouveau) prototype. Il continue d'utiliser l'objet prototype qui était en place lorsqu'il a été instancié.

Vous pouvez corriger cela facilement en commander à nouveau le code, ou en ajoutant des propriétés RequestManager « s prototype plutôt que de le remplacer, par exemple:

RequestManager.prototype.require = function(text){ 
    //make an ajax request 
}; 
RequestManager.prototype.otherFunc = function(){ 
    //do other things 
}; 

Cela fonctionne parce que vous n'avez pas remplacé le objet prototype, vous venez d'y ajouter. myRequestManager voit les ajouts parce que vous les avez ajoutés à l'objet qu'il utilise (plutôt que de définir un nouvel objet sur la propriété prototype de la fonction constructeur). Pourquoi cela arrive est un peu technique et je vais surtout m'en remettre à la spécification. Lorsque l'interpréteur entre dans un nouveau "contexte d'exécution" (par exemple, une fonction, ou le contexte   — par exemple, au niveau de la page   —), l'ordre dans lequel il fait les choses n'est pas strict ordre de la source descendante, il y a des phases . L'une des premières phases consiste à instancier toutes les fonctions définies dans le contexte; cela arrive avant tout code étape par étape est exécuté. Détails dans toute leur gloire dans les sections 10.4.1 (code global), 10.4.3 (code de fonction) et 10.5 (liaison de déclaration) dans the spec, mais fondamentalement, les fonctions sont créées avant la première ligne de code étape par étape.:-)

Ceci est plus facile à voir avec un exemple de test isolé:

<!DOCTYPE HTML> 
<html> 
<head> 
<meta http-equiv="Content-type" content="text/html;charset=UTF-8"> 
<title>Test Page</title> 
<style type='text/css'> 
body { 
    font-family: sans-serif; 
} 
</style> 
<script type='text/javascript'> 
// Uses Thing1 
var User1 = (function() { 
    var thing1 = new Thing1(); 

    function useIt() { 
     alert(thing1.foo()); 
    } 

    return useIt; 
})(); 

// Uses Thing2 
var User2 = (function() { 
    var thing2 = new Thing2(); 

    function useIt() { 
     alert(thing2.foo()); 
    } 

    return useIt; 
})(); 

// Thing1 gets its prototype *replaced* 
function Thing1() { 
    this.name = "Thing1"; 
} 
Thing1.prototype = { 
    foo: function() { 
     return this.name; 
    } 
}; 

// Thing2 gets its prototype *augmented* 
function Thing2() { 
    this.name = "Thing2"; 
} 
Thing2.prototype.foo = function() { 
    return this.name; 
}; 

// Set up to use them 
window.onload = function() { 
    document.getElementById('btnGo').onclick = go; 
} 

// Test! 
function go() { 

    alert("About to use User1"); 
    try 
    { 
     User1(); 
    } 
    catch (e) 
    { 
     alert("Error with User1: " + (e.message ? e.message : String(e))); 
    } 

    alert("About to use User2"); 
    try 
    { 
     User2(); 
    } 
    catch (e) 
    { 
     alert("Error with User2: " + (e.message ? e.message : String(e))); 
    } 
} 

</script> 
</head> 
<body><div> 
<div id='log'></div> 
<input type='button' id='btnGo' value='Go'> 
</div></body> 
</html> 

Comme vous pouvez le voir si vous l'exécutez, User1 échoue parce que l'instance Thing1 qu'il utilise ne dispose pas d'une propriété foo (parce que le prototype a été remplacé), mais User2 fonctionne parce que l'instance Thing2 qu'il utilise * fait (parce que le prototype a été augmenté, non remplacé).

+1

+1 Le piège de hissage JavaScript est de nouveau touché! – bobince

+1

Merci pour les explications et les exemples, je n'ai pas fait la différence entre le prototype augmenté et le prototype remplacé. Maintenant ça marche :-) –

+1

@Kucebe: Super! Oui, c'est un subtil. :-) Pas seulement des prototypes, c'est l'ordre dans lequel les choses se passent qui vous mordront parfois (ça a certainement * moi *). –

Questions connexes