Все мы знаем, что глобальные переменные - это совсем не лучшая практика. Но есть несколько случаев, когда без них сложно кодировать. Какие методы вы используете, чтобы избежать использования глобальных переменных?
Например, учитывая следующий сценарий, как бы вы не использовали глобальную переменную?
Код 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();
}
Соответствующая разметка:
<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. -->
Этот код взят из веб-формы с несколькими <input type="file">
. Он загружает файлы по одному, чтобы предотвратить огромные запросы. Он делает это путем POST в iframe, ожидая ответа, который запускает загрузку iframe, а затем запускает следующую отправку.
Вам не нужно специально отвечать на этот пример, я просто привожу его для ссылки на ситуацию, в которой я не могу придумать способ избежать глобальных переменных.
источник
Ответы:
Самый простой способ - заключить ваш код в закрытие и вручную выставить только те переменные, которые вам нужны глобально, для глобальной области видимости:
(function() { // Your code here // Expose to global window['varName'] = varName; })();
Чтобы ответить на комментарий Crescent Fresh: чтобы полностью удалить глобальные переменные из сценария, разработчику потребуется изменить ряд вещей, предполагаемых в вопросе. Это выглядело бы намного так:
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>
Вам не нужен встроенный обработчик событий
<iframe>
, он все равно будет запускаться при каждой загрузке с этим кодом.Что касается события загрузки
Вот тестовый пример, демонстрирующий, что вам не нужно встроенное
onload
событие. Это зависит от ссылки на файл (/emptypage.php) на том же сервере, иначе вы сможете просто вставить его на страницу и запустить.<!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>
Предупреждение срабатывает каждый раз, когда я нажимаю кнопку отправки в Safari, Firefox, IE 6, 7 и 8.
источник
var foo = "bar"; (function() { alert(window['foo']) })();
Предлагаю шаблон модуля .
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
источник
YAHOO.myProject.myModule
, который является глобальной переменной. Правильно?Во-первых, невозможно избежать глобального JavaScript, что-то всегда будет болтаться в глобальной области видимости. Даже если вы создадите пространство имен, что по-прежнему является хорошей идеей, это пространство имен будет глобальным.
Однако существует множество подходов к тому, чтобы не злоупотреблять глобальным масштабом. Два из самых простых - это либо использовать закрытие, либо, поскольку у вас есть только одна переменная, которую вам нужно отслеживать, просто установите ее как свойство самой функции (которую затем можно рассматривать как
static
переменную).Закрытие
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(); }; })();
* Обратите внимание, что увеличение
uploadCount
здесь происходит внутриСвойство функции
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(); };
Я не уверен, зачем
uploadCount++; if(uploadCount > 1) ...
это нужно, так как похоже, что условие всегда будет выполняться. Но если вам действительно нужен глобальный доступ к переменной, то метод свойства функции, описанный выше, позволит вам сделать это без фактического глобального использования переменной.<iframe src="test.htm" name="postHere" id="postHere" onload="startUpload.count++; if (startUpload.count > 1) startUpload();"></iframe>
Однако в этом случае вам, вероятно, следует использовать литерал объекта или экземпляр объекта и делать это обычным способом объектно-ориентированного программирования (где вы можете использовать шаблон модуля, если он вам нравится).
источник
startUpload
это та единственная переменная, на которую вы ссылаетесь. Удалениеvar startUpload =
из примера закрытия означает, что внутренняя функция никогда не сможет быть выполнена, потому что на нее нет ссылки. Проблема предотвращения загрязнения глобальной области видимости связана с внутренней переменной счетчика,uploadCount
которую используетstartUpload
. Более того, я думаю, что OP пытается избежать загрязнения любой области за пределами этого метода внутреннейuploadCount
переменной.Иногда имеет смысл иметь глобальные переменные в JavaScript. Но не оставляйте их вот так свисать прямо с окна.
Вместо этого создайте единый объект «пространство имен», содержащий ваши глобальные переменные. Чтобы получить бонусные баллы, положите туда все, включая ваши методы.
источник
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); } }
источник
onload
обработчика iframe? Это очень важно.onload
требуется встроенный обработчик. Программно назначить обработчик не получится, потому что он срабатывает только в первый раз. Встроенный обработчик срабатывает каждый раз (по любой причине).Другой способ сделать это - создать объект, а затем добавить к нему методы.
var object = { a = 21, b = 51 }; object.displayA = function() { console.log(object.a); }; object.displayB = function() { console.log(object.b); };
Таким образом, доступен только объект obj и прикрепленные к нему методы. Это эквивалентно добавлению его в пространство имен.
источник
Некоторые вещи будут находиться в глобальном пространстве имен, а именно, любая функция, которую вы вызываете из своего встроенного кода JavaScript.
В общем, решение состоит в том, чтобы завернуть все в укупорку:
(function() { var uploadCount = 0; function startupload() { ... } document.getElementById('postHere').onload = function() { uploadCount ++; if (uploadCount > 1) startUpload(); }; })();
и избегайте встроенного обработчика.
источник
iframe.onload = ...
не эквивалентно<iframe onload="..."
?Замыкания подходят для небольших и средних проектов. Однако для больших проектов вы можете разделить код на модули и сохранить их в разных файлах.
Поэтому я написал плагин jQuery Secret для решения проблемы.
В вашем случае с этим плагином код будет выглядеть примерно так.
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; } }
Соответствующая разметка:
<iframe src="test.htm" name="postHere" id="postHere" onload="$.secret( 'call', 'iframeOnload' );"></iframe>
Откройте свой Firebug , вы вообще не найдете глобалов, даже функции :)
Полную документацию см. Здесь .
Для демонстрационной страницы, пожалуйста, посмотрите это .
Исходный код на GitHub .
источник
Используйте закрытия. Что-то вроде этого дает вам область, отличную от глобальной.
(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 })();
источник
Для "защиты" индивидуальных глобальных переменных:
function gInitUploadCount() { var uploadCount = 0; gGetUploadCount = function () { return uploadCount; } gAddUploadCount= function () { uploadCount +=1; } } gInitUploadCount(); gAddUploadCount(); console.log("Upload counter = "+gGetUploadCount());
Я новичок в JS, в настоящее время использую это в одном проекте. (ценю любые комментарии и критику)
источник
Я использую это так:
{ var globalA = 100; var globalB = 200; var globalFunc = function() { ... } let localA = 10; let localB = 20; let localFunc = function() { ... } localFunc(); }
Для всех глобальных областей используйте 'var', а для локальных областей используйте 'let'.
источник