Wir alle wissen, dass globale Variablen alles andere als bewährte Verfahren sind. Es gibt jedoch mehrere Fälle, in denen es schwierig ist, ohne sie zu codieren. Welche Techniken verwenden Sie, um die Verwendung globaler Variablen zu vermeiden?
Wie würden Sie beispielsweise im folgenden Szenario keine globale Variable verwenden?
JavaScript-Code:
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();
}
Relevantes Markup:
<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. -->
Dieser Code stammt aus einem Webformular mit mehreren <input type="file">
. Die Dateien werden einzeln hochgeladen, um große Anforderungen zu vermeiden. Dazu wird ein POST an den Iframe gesendet, auf die Antwort gewartet, die den Iframe-Onload auslöst, und dann die nächste Übermittlung ausgelöst.
Sie müssen dieses Beispiel nicht speziell beantworten. Ich verweise nur auf eine Situation, in der ich mir keine Möglichkeit vorstellen kann, globale Variablen zu vermeiden.
quelle
Antworten:
Am einfachsten ist es, Ihren Code in einen Abschluss zu packen und nur die Variablen, die Sie global benötigen, manuell für den globalen Bereich verfügbar zu machen:
(function() { // Your code here // Expose to global window['varName'] = varName; })();
Um den Kommentar von Crescent Fresh anzusprechen: Um globale Variablen vollständig aus dem Szenario zu entfernen, müsste der Entwickler eine Reihe von in der Frage angenommenen Dingen ändern. Es würde viel mehr so aussehen:
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>
Sie müssen nicht brauchen einen Inline - Event - Handler auf das
<iframe>
, wird es immer noch das Feuer auf jede Last mit diesem Code.In Bezug auf das Ladeereignis
Hier ist ein Testfall, der zeigt, dass Sie kein Inline-
onload
Ereignis benötigen . Dies hängt davon ab, ob Sie auf eine Datei (/emptypage.php) auf demselben Server verweisen. Andernfalls sollten Sie diese einfach in eine Seite einfügen und ausführen können.<!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: // http://stackoverflow.com/q/875650 var iframe; try { iframe = document.createElement('<iframe name="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>
Die Warnung wird jedes Mal ausgelöst, wenn ich in Safari, Firefox, IE 6, 7 und 8 auf die Schaltfläche "Senden" klicke.
quelle
var foo = "bar"; (function() { alert(window['foo']) })();
Ich schlage das Modulmuster vor .
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
quelle
YAHOO.myProject.myModule
einer globalen Variablen gespeichert . Richtig?Zunächst einmal ist es unmöglich, globales JavaScript zu vermeiden, etwas wird immer den globalen Bereich baumeln lassen. Selbst wenn Sie einen Namespace erstellen, was immer noch eine gute Idee ist, ist dieser Namespace global.
Es gibt jedoch viele Ansätze, um den globalen Geltungsbereich nicht zu missbrauchen. Zwei der einfachsten sind, entweder den Abschluss zu verwenden, oder da Sie nur eine Variable haben, die Sie verfolgen müssen, legen Sie sie einfach als Eigenschaft der Funktion selbst fest (die dann als
static
Variable behandelt werden kann).Schließung
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(); }; })();
* Beachten Sie, dass das Inkrementieren
uploadCount
hier intern erfolgtFunktionseigenschaft
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(); };
Ich bin mir nicht sicher, warum dies
uploadCount++; if(uploadCount > 1) ...
notwendig ist, da es so aussieht, als ob die Bedingung immer wahr sein wird. Wenn Sie jedoch globalen Zugriff auf die Variable benötigen, können Sie dies mit der oben beschriebenen Funktionseigenschaftsmethode tun, ohne dass die Variable tatsächlich global ist.<iframe src="test.htm" name="postHere" id="postHere" onload="startUpload.count++; if (startUpload.count > 1) startUpload();"></iframe>
Wenn dies jedoch der Fall ist, sollten Sie wahrscheinlich ein Objektliteral oder ein instanziiertes Objekt verwenden und dies auf normale OO-Weise tun (wobei Sie das Modulmuster verwenden können, wenn es Ihnen gefällt).
quelle
startUpload
handelt es sich um die einzelne Variable, auf die Sie sich beziehen. Das Entfernenvar startUpload =
aus dem Schließungsbeispiel bedeutet, dass die innere Funktion niemals ausgeführt werden kann, da kein Verweis darauf vorhanden ist. Das Problem der Vermeidung einer Verschmutzung durch den globalen Geltungsbereich betrifft die interne Zählervariable,uploadCount
die von verwendet wirdstartUpload
. Darüber hinaus denke ich, dass das OP versucht, zu vermeiden, dass ein Bereich außerhalb dieser Methode mit der intern verwendetenuploadCount
Variablen verschmutzt wird .Manchmal ist es sinnvoll, globale Variablen in JavaScript zu haben. Aber lassen Sie sie nicht so direkt am Fenster hängen.
Erstellen Sie stattdessen ein einzelnes "Namespace" -Objekt, das Ihre Globals enthält. Geben Sie für Bonuspunkte alles ein, einschließlich Ihrer Methoden.
quelle
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); } }
quelle
onload
Handler auf dem Iframe? Das ist entscheidend.onload
Handler erforderlich. Das programmgesteuerte Zuweisen des Handlers funktioniert nicht, da er nur beim ersten Mal ausgelöst wird. Der Inline-Handler wird jedes Mal ausgelöst (aus welchem Grund auch immer).Eine andere Möglichkeit, dies zu tun, besteht darin, ein Objekt zu erstellen und ihm dann Methoden hinzuzufügen.
var object = { a = 21, b = 51 }; object.displayA = function() { console.log(object.a); }; object.displayB = function() { console.log(object.b); };
Auf diese Weise wird nur das Objekt 'obj' verfügbar gemacht und Methoden daran angehängt. Dies entspricht dem Hinzufügen im Namespace.
quelle
Einige Dinge werden sich im globalen Namespace befinden - nämlich welche Funktion Sie aus Ihrem Inline-JavaScript-Code aufrufen.
Im Allgemeinen besteht die Lösung darin, alles in einen Verschluss zu wickeln:
(function() { var uploadCount = 0; function startupload() { ... } document.getElementById('postHere').onload = function() { uploadCount ++; if (uploadCount > 1) startUpload(); }; })();
und vermeiden Sie den Inline-Handler.
quelle
iframe.onload = ...
ist nicht gleichbedeutend mit<iframe onload="..."
?Die Verwendung von Schließungen kann für kleine bis mittlere Projekte in Ordnung sein. Bei großen Projekten möchten Sie Ihren Code jedoch möglicherweise in Module aufteilen und in verschiedenen Dateien speichern.
Deshalb habe ich das jQuery Secret Plugin geschrieben , um das Problem zu lösen.
In Ihrem Fall mit diesem Plugin würde der Code ungefähr so aussehen.
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; } }
Relevantes Markup:
<iframe src="test.htm" name="postHere" id="postHere" onload="$.secret( 'call', 'iframeOnload' );"></iframe>
Öffne deinen Firebug , du wirst überhaupt keine Globals finden, nicht einmal die Funktion :)
Eine vollständige Dokumentation finden Sie hier .
Eine Demoseite finden Sie hier .
Quellcode auf GitHub .
quelle
Verwenden Sie Verschlüsse. So etwas gibt Ihnen einen anderen Bereich als global.
(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 })();
quelle
Zum "Sichern" einzelner globaler Variablen:
function gInitUploadCount() { var uploadCount = 0; gGetUploadCount = function () { return uploadCount; } gAddUploadCount= function () { uploadCount +=1; } } gInitUploadCount(); gAddUploadCount(); console.log("Upload counter = "+gGetUploadCount());
Ich bin ein Neuling bei JS und verwende dies derzeit in einem Projekt. (Ich schätze jeden Kommentar und jede Kritik)
quelle
Ich benutze es so:
{ var globalA = 100; var globalB = 200; var globalFunc = function() { ... } let localA = 10; let localB = 20; let localFunc = function() { ... } localFunc(); }
Verwenden Sie für alle globalen Bereiche 'var' und für lokale Bereiche 'let'.
quelle