Comment éviter les variables globales en JavaScript?

Nous savons tous que variables globales sont rien mais le meilleur dans la pratique. Mais il y a plusieurs cas où il est difficile de coder sans eux. Quelles techniques utilisez-vous pour éviter l'utilisation de variables globales?

par exemple, étant donné le scénario suivant, Comment ne pas utiliser une variable globale?

code JavaScript:

var uploadCount = 0;

window.onload = function() {
    var frm = document.forms[0];

    frm.target = "postMe";
    frm.onsubmit = function() {
        startUpload();
        return false;
    }
}

function startUpload() {
    var fil = document.getElementById("FileUpload" + uploadCount);

    if (!fil || fil.value.length == 0) {
        alert("Finished!");
        document.forms[0].reset();
        return;
    }

    disableAllFileInputs();
    fil.disabled = false;
    alert("Uploading file " + uploadCount);
    document.forms[0].submit();
}

majoration:

<iframe src="test.htm" name="postHere" id="postHere"
  onload="uploadCount++; if(uploadCount > 1) startUpload();"></iframe>

<!-- MUST use inline JavaScript here for onload event
     to fire after each form submission. -->

ce code provient d'un formulaire Web avec plusieurs <input type="file"> . Il télécharge les fichiers un à la fois afin d'éviter d'énormes demandes. Il le fait par POST ing à l'iframe, en attendant la réponse qui déclenche l'iframe onload, et puis déclenche la soumission suivante.

Vous n'avez pas à répondre à cet exemple en particulier, je suis juste à fournir pour la référence à une situation dans laquelle je suis incapable penser à un moyen d'éviter les variables globales.

68
demandé sur Peter Mortensen 2009-12-03 21:30:15

10 réponses

la manière la plus simple est d'envelopper votre code dans une fermeture et n'exposer manuellement que les variables dont vous avez besoin globalement à la portée globale:

(function() {
    // Your code here

    // Expose to global
    window['varName'] = varName;
})();

pour répondre au commentaire de Crescent Fresh: afin de supprimer entièrement les variables globales du scénario, le développeur devrait changer un certain nombre de choses supposées dans la question. Il ressemblerait beaucoup plus à ceci:

Javascript:

(function() {
    var addEvent = function(element, type, method) {
        if('addEventListener' in element) {
            element.addEventListener(type, method, false);
        } else if('attachEvent' in element) {
            element.attachEvent('on' + type, method);

        // If addEventListener and attachEvent are both unavailable,
        // use inline events. This should never happen.
        } else if('on' + type in element) {
            // If a previous inline event exists, preserve it. This isn't
            // tested, it may eat your baby
            var oldMethod = element['on' + type],
                newMethod = function(e) {
                    oldMethod(e);
                    newMethod(e);
                };
        } else {
            element['on' + type] = method;
        }
    },
        uploadCount = 0,
        startUpload = function() {
            var fil = document.getElementById("FileUpload" + uploadCount);

            if(!fil || fil.value.length == 0) {    
                alert("Finished!");
                document.forms[0].reset();
                return;
            }

            disableAllFileInputs();
            fil.disabled = false;
            alert("Uploading file " + uploadCount);
            document.forms[0].submit();
        };

    addEvent(window, 'load', function() {
        var frm = document.forms[0];

        frm.target = "postMe";
        addEvent(frm, 'submit', function() {
            startUpload();
            return false;
        });
    });

    var iframe = document.getElementById('postHere');
    addEvent(iframe, 'load', function() {
        uploadCount++;
        if(uploadCount > 1) {
            startUpload();
        }
    });

})();

HTML:

<iframe src="test.htm" name="postHere" id="postHere"></iframe>

Vous n'avez pas besoin inline gestionnaire d'événements sur la <iframe> , il sera toujours le feu sur chaque charge avec ce code.

concernant l'événement de charge

voici un cas d'essai démontrant que vous n'avez pas besoin d'un événement onload . Cela dépend de la référence d'un fichier (/emptypage.php) sur le même serveur, sinon vous devriez pouvoir simplement coller ceci dans un page et de l'exécuter.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>untitled</title>
</head>
<body>
    <script type="text/javascript" charset="utf-8">
        (function() {
            var addEvent = function(element, type, method) {
                if('addEventListener' in element) {
                    element.addEventListener(type, method, false);
                } else if('attachEvent' in element) {
                    element.attachEvent('on' + type, method);

                    // If addEventListener and attachEvent are both unavailable,
                    // use inline events. This should never happen.
                } else if('on' + type in element) {
                    // If a previous inline event exists, preserve it. This isn't
                    // tested, it may eat your baby
                    var oldMethod = element['on' + type],
                    newMethod = function(e) {
                        oldMethod(e);
                        newMethod(e);
                    };
                } else {
                    element['on' + type] = method;
                }
            };

            // Work around IE 6/7 bug where form submission targets
            // a new window instead of the iframe. SO suggestion here:
            // /q/why-does-internet-explorer-open-form-submission-in-a-new-window-and-not-in-a-dynamically-inserted-iframe-43861/"postHere">');
            } catch (e) {
                iframe = document.createElement('iframe');
                iframe.name = 'postHere';
            }

            iframe.name = 'postHere';
            iframe.id = 'postHere';
            iframe.src = '/emptypage.php';
            addEvent(iframe, 'load', function() {
                alert('iframe load');
            });

            document.body.appendChild(iframe);

            var form = document.createElement('form');
            form.target = 'postHere';
            form.action = '/emptypage.php';
            var submit = document.createElement('input');
            submit.type = 'submit';
            submit.value = 'Submit';

            form.appendChild(submit);

            document.body.appendChild(form);
        })();
    </script>
</body>
</html>

l'alerte s'allume chaque fois que je clique sur le bouton Soumettre dans Safari, Firefox, i.e. 6, 7 et 8.

63
répondu eyelidlessness 2011-11-04 18:22:11

je suggère le module pattern .

YAHOO.myProject.myModule = function () {

    //"private" variables:
    var myPrivateVar = "I can be accessed only from within YAHOO.myProject.myModule.";

    //"private" method:
    var myPrivateMethod = function () {
        YAHOO.log("I can be accessed only from within YAHOO.myProject.myModule");
    }

    return  {
        myPublicProperty: "I'm accessible as YAHOO.myProject.myModule.myPublicProperty."
        myPublicMethod: function () {
            YAHOO.log("I'm accessible as YAHOO.myProject.myModule.myPublicMethod.");

            //Within myProject, I can access "private" vars and methods:
            YAHOO.log(myPrivateVar);
            YAHOO.log(myPrivateMethod());

            //The native scope of myPublicMethod is myProject; we can
            //access public members using "this":
            YAHOO.log(this.myPublicProperty);
        }
    };

}(); // the parens here cause the anonymous function to execute and return
54
répondu erenon 2009-12-03 18:38:10

tout d'abord, il est impossible d'éviter le JavaScript global, quelque chose sera toujours pendouiller la portée globale. Même si vous créez un namespace, qui est toujours une bonne idée, ce namespace sera global.

il existe cependant de nombreuses approches pour ne pas abuser de la portée mondiale. Deux des plus simples sont soit d'utiliser la fermeture, ou puisque vous n'avez qu'une seule variable dont vous devez tenir compte, il suffit de la définir comme une propriété de la fonction elle-même (qui peut alors être traitée comme un static variable).

fermeture

var startUpload = (function() {
  var uploadCount = 1;  // <----
  return function() {
    var fil = document.getElementById("FileUpload" + uploadCount++);  // <----

    if(!fil || fil.value.length == 0) {    
      alert("Finished!");
      document.forms[0].reset();
      uploadCount = 1; // <----
      return;
    }

    disableAllFileInputs();
    fil.disabled = false;
    alert("Uploading file " + uploadCount);
    document.forms[0].submit();
  };
})();

* notez que l'incrémentation de uploadCount se produit à l'interne ici

Fonction Propriété

var startUpload = function() {
  startUpload.uploadCount = startUpload.count || 1; // <----
  var fil = document.getElementById("FileUpload" + startUpload.count++);

  if(!fil || fil.value.length == 0) {    
    alert("Finished!");
    document.forms[0].reset();
    startUpload.count = 1; // <----
    return;
  }

  disableAllFileInputs();
  fil.disabled = false;
  alert("Uploading file " + startUpload.count);
  document.forms[0].submit();
};

Je ne sais pas pourquoi uploadCount++; if(uploadCount > 1) ... est nécessaire, car il semble que la condition sera toujours vrai. Mais si vous avez besoin d'un accès global à la variable, alors la propriété de la fonction méthode que j'ai décrite ci-dessus va vous permettent de le faire sans la variable réellement mondial.

<iframe src="test.htm" name="postHere" id="postHere"
  onload="startUpload.count++; if (startUpload.count > 1) startUpload();"></iframe>

cependant, si c'est le cas, alors vous devriez probablement utiliser un objet littéral ou instancié et aller à ce sujet dans la voie OO normale (où vous pouvez utiliser le modèle de module si elle frappe votre fantaisie).

7
répondu Justin Johnson 2014-07-15 16:37:05
window.onload = function() {
  var frm = document.forms[0];
  frm.target = "postMe";
  frm.onsubmit = function() {
    frm.onsubmit = null;
    var uploader = new LazyFileUploader();
    uploader.startUpload();
    return false;
  }
}

function LazyFileUploader() {
    var uploadCount = 0;
    var total = 10;
    var prefix = "FileUpload";  
    var upload = function() {
        var fil = document.getElementById(prefix + uploadCount);

        if(!fil || fil.value.length == 0) {    
            alert("Finished!");
            document.forms[0].reset();
            return;
         }

        disableAllFileInputs();
        fil.disabled = false;
        alert("Uploading file " + uploadCount);
        document.forms[0].submit();
        uploadCount++;

        if (uploadCount < total) {
            setTimeout(function() {
                upload();
            }, 100); 
        }
    }

    this.startUpload = function() {
        setTimeout(function() {
            upload();
        }, 100);  
    }       
}
4
répondu ChaosPandion 2009-12-03 18:46:05

il est parfois logique d'avoir des variables globales en JavaScript. Mais ne les laissez pas pendre directement à la fenêtre comme ça.

à la place, créez un seul objet "namespace" pour contenir vos globals. Pour les points bonus, mettez tout là-dedans, y compris vos méthodes.

3
répondu Nosredna 2009-12-03 18:36:28

certaines choses vont être dans l'espace de noms global -- à savoir, quelle que soit la fonction que vous appelez à partir de votre code JavaScript en ligne.

en général, la solution est de tout envelopper dans une fermeture:

(function() {
    var uploadCount = 0;
    function startupload() {  ...  }
    document.getElementById('postHere').onload = function() {
        uploadCount ++;
        if (uploadCount > 1) startUpload();
    };
})();

et éviter le gestionnaire en ligne.

1
répondu Jimmy 2012-05-19 17:29:29

L'utilisation de fermetures pourrait être acceptable pour les projets de petite à moyenne envergure. Cependant, pour les grands projets, vous pourriez vouloir diviser votre code en modules et les enregistrer dans des fichiers différents.

donc j'ai écrit jQuery secret plugin pour résoudre le problème.

dans votre cas avec ce plugin le code ressemblerait à quelque chose comme ceci.

JavaScript:

// Initialize uploadCount.
$.secret( 'in', 'uploadCount', 0 ).

// Store function disableAllFileInputs.
secret( 'in', 'disableAllFileInputs', function(){
  // Code for 'disable all file inputs' goes here.

// Store function startUpload
}).secret( 'in', 'startUpload', function(){
    // 'this' points to the private object in $.secret
    // where stores all the variables and functions
    // ex. uploadCount, disableAllFileInputs, startUpload.

    var fil = document.getElementById( 'FileUpload' + uploadCount);

    if(!fil || fil.value.length == 0) {
        alert( 'Finished!' );
        document.forms[0].reset();
        return;
    }

    // Use the stored disableAllFileInputs function
    // or you can use $.secret( 'call', 'disableAllFileInputs' );
    // it's the same thing.
    this.disableAllFileInputs();
    fil.disabled = false;

    // this.uploadCount is equal to $.secret( 'out', 'uploadCount' );
    alert( 'Uploading file ' + this.uploadCount );
    document.forms[0].submit();

// Store function iframeOnload
}).secret( 'in', 'iframeOnload', function(){
    this.uploadCount++;
    if( this.uploadCount > 1 ) this.startUpload();
});

window.onload = function() {
    var frm = document.forms[0];

    frm.target = "postMe";
    frm.onsubmit = function() {
        // Call out startUpload function onsubmit
        $.secret( 'call', 'startUpload' );
        return false;
    }
}

pertinent markup:

<iframe src="test.htm" name="postHere" id="postHere" onload="$.secret( 'call', 'iframeOnload' );"></iframe>

ouvrez votre Firebug , vous ne trouverez pas de globals du tout, même pas le funciton :)

pour la documentation complète, voir ici .

pour une page Démo, voir this .

code Source sur GitHub .

1
répondu ben 2012-05-19 18:17:37

une autre façon de faire ceci est de créer un objet et d'y ajouter des méthodes.

var object = {
  a = 21,
  b = 51
};

object.displayA = function() {
 console.log(object.a);
};

object.displayB = function() {
 console.log(object.b);
};

De cette façon, seul l'objet 'obj' est exposée et les méthodes associées. Il est équivalent à l'ajouter dans namespace.

1
répondu Ajay Poshak 2017-10-06 09:58:21

utiliser des fermetures. Quelque chose comme ça vous donne une portée autre que globale.

(function() {
    // Your code here
    var var1;
    function f1() {
        if(var1){...}
    }

    window.var_name = something; //<- if you have to have global var
    window.glob_func = function(){...} //<- ...or global function
})();
0
répondu NilColor 2009-12-03 22:37:22

pour "sécurisation" des variables globales individuelles:

function gInitUploadCount() {
    var uploadCount = 0;

    gGetUploadCount = function () {
        return uploadCount; 
    }
    gAddUploadCount= function () {
        uploadCount +=1;
    } 
}

gInitUploadCount();
gAddUploadCount();

console.log("Upload counter = "+gGetUploadCount());

je suis un novice de JS, actuellement en utilisant ceci dans un projet. (j'ai aprécier tout commentaire et critique)

0
répondu Koenees 2016-09-14 06:37:35