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.
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.
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
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).
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);
}
}
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.
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.
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 .
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.
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
})();
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)