How to avoid global variables in JavaScript?

We all know that global variables are not best practice. But there are several cases when it is difficult to code with them. What methods do you use to avoid using global variables?

For example, given the following scenario, how would you use a global variable?

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

Corresponding 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. --> 

This code comes from a web form with several <input type="file"> . It downloads files one at a time to prevent huge requests. He does this using POST in the iframe, waiting for a response that triggers the iframe onload, and then triggers the next send.

You do not have to answer this example, I just provide it to refer to a situation in which I cannot think of a way to avoid global variables.

+67
javascript global-variables
Dec 03 '09 at 18:30
source share
10 answers

The easiest way is to wrap your code in a closure and manually set only those variables that you need globally for the global scope:

 (function() { // Your code here // Expose to global window['varName'] = varName; })(); 

To send a message to Crescent Fresh: in order to completely remove global variables from the script, the developer would need to change a number of things accepted in the question. It would look a lot bigger:

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> 

You don't need the built-in event handler in the <iframe> , it will still trigger every load with this code.

Regarding the boot event

Here is a test case demonstrating that you do not need the onload built-in event. It depends on the link to the file (/emptypage.php) on the same server, otherwise you can just insert it into the page and run it.

 <!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> 

A warning is triggered every time I click the submit button in Safari, Firefox, IE 6, 7, and 8.

+63
Dec 03 '09 at 18:34
source share

I suggest a module template .

 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 
+52
Dec 03 '09 at 18:38
source share

Firstly, global JavaScript cannot be avoided; something will always chat in the global area. Even if you create a namespace, which is still a good idea, that namespace will be global.

However, there are many approaches not to abuse global reach. Two of the easiest are either to use closure, or since you have only one variable that you need to track, just set it as a property of the function itself (which can then be considered as a static variable).

Closing

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

* Note that the uploadCount increment happens inside here

Function property

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

I am not sure why uploadCount++; if(uploadCount > 1) ... uploadCount++; if(uploadCount > 1) ... necessary because it looks like the condition will always be true. But if you need global access to a variable, then the function property method described above will allow you to do this without an actual global variable.

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

However, if this is the case, then you should probably use an object literal or an instance of the object and work around this in the usual OO way (where you can use the module template if it is amazing).

+7
Dec 03 '09 at 20:30
source share
 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
Dec 03 '09 at 18:39
source share

Sometimes it makes sense to have global variables in JavaScript. But do not leave them hanging right from the window.

Instead, create a single namespace object containing your global variables. For bonus points, put everything there, including your methods.

+3
Dec 03 '09 at 18:36
source share

Some things will be in the global namespace, namely, which function you are calling from the built-in JavaScript code.

In general, the solution is to wrap everything in a closure:

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

and avoid the inline handler.

+1
Dec 03 '09 at 18:34
source share

Using closures may be OK for small to medium sized projects. However, for large projects, you can split your code into modules and save them in different files.

So I decided to write a jQuery Secret plugin to solve this problem.

In your case with this plugin, the code will look something like this:

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 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; } } 

Corresponding markup:

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

Open Firebug , you will not find any global variables, not even funciton :)

For full documentation see here .

For a demo page see this .

GitHub source code .

+1
Apr 6 '11 at 8:17
source share

Another way to do this is to create an object and then add methods to it.

 var object = { a = 21, b = 51 }; object.displayA = function() { console.log(object.a); }; object.displayB = function() { console.log(object.b); }; 

Thus, only the 'obj' object and the methods attached to it are opened. This is equivalent to adding it to the namespace.

+1
Oct 06 '17 at 9:58 on
source share

Use closure. Something like this gives you an area other than 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
Dec 03 '09 at 22:37
source share

To "protect" induvidual global variables:

 function gInitUploadCount() { var uploadCount = 0; gGetUploadCount = function () { return uploadCount; } gAddUploadCount= function () { uploadCount +=1; } } gInitUploadCount(); gAddUploadCount(); console.log("Upload counter = "+gGetUploadCount()); 

I am new to JS, currently using this in one project. (I appreciate any comments and criticism)

0
Sep 14 '16 at 6:37
source share



All Articles