2010-11-04 7 views
4

Désolé si cela a déjà été répondu, mais je ne pouvais pas trouver une réponse appropriée ici.Portée variable dans modulaire Javascript

J'ai commencé à écrire mon code javascript dans un style modulaire dernièrement et j'ai une question concernant le fonctionnement de la variable variable du module.

Le code suivant me donne une réponse contradictoire.

J'ai un module nommé Base qui déclare deux chaînes et un tableau. Il a également une fonction appelée fetchData qui utilise le raccourci jQuery getJSON pour définir ces variables avec les données du serveur. Malheureusement, lorsque je demande la chaîne string1 ou string2 de Base, je ne suis pas définie. Je comprends que cela est probablement dû au fait que je lui ai donné deux valeurs profondes à leurs valeurs (à l'intérieur du callback AJAX et à l'intérieur de fetchData) et la portée le limite en voyant Base.string1 et Base.string2. Cependant, lorsque je regarde Base.array1 depuis l'extérieur du module, il est défini sur les données appropriées que j'ai extraites du serveur, même si elles ont la même portée que les chaînes.

Voici le code:

namespace.Base = (function(){ 
    var string1, string2, array1 = []; 
    function fetchData(){ 
     $.getJSON('backendScript.php', function(data){ 
      string1 = data.string1; 
      string2 = data.string2; 
       arrayCount = data.arr.length; 
       for(var i = 0; i<arrayCount; i++){ 
        array1[i] = data.arr[i]; 
       } 
     }) 
    } 
    return{ 
     fetchData: fetchData, 
     string1: string1, 
     string2: string2, 
     array1: array1 
    } 
})(); 

Si je change

string1 = data.string1; 

à

namespace.Base.string1 = data.string1; 

cela fonctionne comme je veux. Donc, ma question est, pourquoi est-ce que array1 est correctement défini quand il est défini dans la même portée que les chaînes?

De même, quel est le remède pour la définition de variables de niveau module à partir des fonctions du module sans avoir à donner un chemin global (par exemple namespace.Base.string1)?

+0

Souvenez-vous de upvote * toutes * réponses qui vous sont utiles. Cochez celui qui répond le mieux à votre question. Si aucun n'est «vérifiable» alors faites juste les upvotes pour toutes les réponses utiles. –

+0

Je suis nouveau ici, donc je vais upvote dès que j'ai 15 réputation. – Dan

+0

Hmmm, bon point. Bienvenue dans Stack Overflow. –

Répondre

4

Le problème est que vous avez en fait deux références différentes, la string1 variable dans la fermeture de la fonction anonyme que vous invoquez pour créer namespace.Base et namespace.Base.string1, qui se trouve sur l'objet retourné par cette fonction anonyme. L'affectation de la variable string1 à la propriété d'objet string1 est un ensemble unique et non une référence dynamique. Une modification supplémentaire de la variable string1 n'affectera pas la propriété de l'objet. Voici ce que vous voulez:

namespace.Base = (function() { 
    var my = { 
    string1: null, 
    string2: null, 
    array1: [], 
    fetchData: function() { 
     $.getJSON('backendScript.php', function(data){ 
     my.string1 = data.string1; 
     my.string2 = data.string2; 
     var arrayCount = data.arr.length; 
     for (var i = 0; i < arrayCount; i++){ 
      my.array1[i] = data.arr[i]; 
     } 
     }); 
    } 
    }; 
    return my; 
})(); 

Maintenant, les locaux, mais publics, les membres de namespace.Base sont dans l'objet my. Vous pouvez créer des variables privées à l'aide de var dans la fonction anonyme ou créer d'autres propriétés publiques en les ajoutant à .

+0

Merci! Je peux comprendre comment mes variables ont été renvoyées avant d'être définies par fetchData. Je les considérais incorrectement comme des références vivantes ou des propriétés similaires d'un objet. Je commence à penser que le modèle de module ne vaut pas la peine d'être utilisé par opposition à la création de choses en tant qu'objets. Je comprends le concept d'avoir des variables privées et de choisir quelles variables autoriser l'accès via une API (le retour), mais il semble que pour créer des variables dynamiques, je dois quand même utiliser le modèle d'objet. – Dan

+0

En outre, la création d'un objet nommé "my" est-elle une pratique standard lors de la création de modules? Je me souviens de l'avoir déjà vu et de ne pas le comprendre complètement. – Dan

+0

J'utilise souvent 'my' à cette fin. Parfois, cela ou d'autres termes sont utilisés. Je pense que 'my' est un peu plus clair. Il est vrai que le modèle de module est imparfait, car vous devez toujours utiliser des objets pour conserver les références locales aux propriétés publiques. C'est à vous de décider si vous l'utilisez ou non, mais les propriétés privées sont parfois pratiques à avoir. – bcherry

0

Votre problème de "portée" n'est pas réellement un problème de portée. Le problème est que les tableaux sont des pointeurs vers leurs données, les chaînes ne le sont pas. Namespace.Base est défini sur les résultats (valeur renvoyée) de la fonction anonyme. - Il est défini comme un objet contenant une fonction ref (fetchData), deux chaînes vides et un tableau.

Si vous appelez plus tard la fonction fetchData, cela modifiera le contenu de array1. Mais il va aussi créer deux nouvelles chaînes (à partir de data.string1 et data.string2).Les anciennes valeurs de string1 et string2 (qui sont namespace.Base.string1 et namespace.Base.string2) ne sont pas modifiées. Donc, ils sont laissés comme des chaînes vides (pas ce que vous voulez).

Exemple de ceci. Essayez dans Firebug--

s1 = "Hi"; 
s2 = s1; // s2 => "Hi" 
s1 = "Bye" 
alert(s2); // *** s2 is still "Hi", it was not changed! 

// But arrays are different: 
a1 = ["Hi"]; 
a2 = a1; 
a1[0] = "Bye"; 
alert(a2[0]); // a2[0] is now "Bye" 

Ajouté: Asynch Erreur de timing

Notez également que votre code est erroné comme écrit puisque vous ne donnez pas à l'appelant aucune façon de savoir quand l'appel Ajax a complété:

namespace.Base.fetchData(); // starts the Ajax call via getJSON method 
var a = namespace.Base.array1; // ERROR!! The value of namespace.Base.array1 is 
           // indeterminate since you don't know if the 
           // the Ajax request has completed yet or not! 

Vous semblez essayer de convertir asynchrone appel Ajax (qui appelle une fonction de rappel une fois la réponse a été reçue du serveur distant) dans un appel synchrone qui ne reviendra pas avant la r les résultats ont été reçus.

C'est vraiment une mauvaise idée. (Si vous voulez en savoir plus, poser une autre question SO.)

+0

Merci! Je commençais à mieux comprendre les fermetures, mais je ne savais pas non plus que les tableaux fonctionnaient de cette façon ... un exemple très perspicace. – Dan