webentwicklung-frage-antwort-db.com.de

Wie vermeide ich globale Variablen in JavaScript?

Wir alle wissen, dass globale Variablen alles andere als Best Practice sind. Es gibt jedoch mehrere Fälle, in denen es schwierig ist, ohne sie zu programmieren. Welche Techniken verwenden Sie, um die Verwendung globaler Variablen zu vermeiden?

Wie würden Sie in dem folgenden Szenario beispielsweise 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">. Es lädt die Dateien einzeln hoch, um große Anforderungen zu vermeiden. Dies geschieht, indem POST an den Iframe gesendet wird, auf die Antwort gewartet wird, die den Iframe-Onload auslöst, und dann die nächste Übermittlung ausgelöst wird.

Sie müssen dieses Beispiel nicht spezifisch beantworten, sondern geben es nur als Referenz für eine Situation an, in der ich mir keine Möglichkeit vorstellen kann, globale Variablen zu vermeiden.

75
Josh Stodola

Am einfachsten ist es, den Code in einen Closure 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 auf den Kommentar von Crescent Fresh einzugehen: Um globale Variablen vollständig aus dem Szenario zu entfernen, müsste der Entwickler eine Reihe von Dingen ändern, die in der Frage angenommen wurden. 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 benötigen keinen Inline-Event-Handler auf dem <iframe>, es wird weiterhin bei jedem Laden mit diesem Code ausgelöst.

Bezüglich des Ladeereignisses

Hier ist ein Testfall, der zeigt, dass Sie kein Inline-Ereignis onload 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 zum Senden klicke.

64
eyelidlessness

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
56
erenon

Zuallererst ist es unmöglich, globales JavaScript zu vermeiden, etwas wird immer den globalen Rahmen sprengen. Auch 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 Closure oder, da Sie nur eine Variable haben, die Sie im Auge behalten müssen, setzen Sie sie einfach als eine Eigenschaft der Funktion selbst (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 von uploadCount hier intern erfolgt

Function-Eigenschaft

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 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 Funktionseigenschaft -Methode 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 wie gewohnt vorgehen OO (wobei Sie das Modulmuster verwenden können, wenn es Ihnen gefällt) .

7
Justin Johnson

Manchmal ist es sinnvoll, globale Variablen in JavaScript zu haben. Aber lass sie nicht so direkt am Fenster hängen.

Erstellen Sie stattdessen ein einzelnes "Namespace" -Objekt, das Ihre globalen Elemente enthält. Setzen Sie für Bonuspunkte alles ein, einschließlich Ihrer Methoden.

4
Nosredna
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
ChaosPandion

Eine andere Möglichkeit 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.

2
Ajay Poshak

Einige Dinge werden sich im globalen Namespace befinden - nämlich welche Funktion Sie auch immer aus Ihrem Inline-JavaScript-Code aufrufen.

Im Allgemeinen besteht die Lösung darin, alles in einem Verschluss zu verpacken:

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

und vermeiden Sie den Inline-Handler.

1
Jimmy

Die Verwendung von Verschlüssen ist möglicherweise für kleine bis mittlere Projekte in Ordnung. Bei großen Projekten empfiehlt es sich jedoch, den Code in Module aufzuteilen und in verschiedenen Dateien zu speichern.

Deshalb habe ich jQuery Secret plugin geschrieben, um das Problem zu lösen.

In Ihrem Fall würde der Code mit diesem Plugin wie folgt 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;
    }
}

Relevante Auszeichnung:

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

Öffne dein Firebug , du wirst überhaupt keine Globals finden, nicht einmal die Funktion :)

Eine vollständige Dokumentation finden Sie unter hier .

Eine Demo-Seite finden Sie unter this .

Quellcode auf GitHub .

1
ben

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 Anfänger von JS und benutze dies derzeit in einem Projekt. (Ich schätze jeden Kommentar und jede Kritik)

0
Koenees

Verschlüsse verwenden. So etwas gibt Ihnen einen anderen Geltungsbereich 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
})();
0
NilColor