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">
. 대규모 요청을 방지하기 위해 한 번에 하나씩 파일을 업로드합니다. iframe 에 POST ing을 수행하고 iframe onload를 실행하는 응답을 기다린 후 다음 제출을 트리거합니다.
이 예제에 구체적으로 대답 할 필요는 없습니다. 전역 변수를 피하는 방법을 생각할 수없는 상황을 참조하기 위해 제공하는 것입니다.
가장 쉬운 방법은 코드를 클로저로 래핑하고 전역 적으로 필요한 변수 만 전역 범위에 수동으로 노출하는 것입니다.
(function() {
// Your code here
// Expose to global
window['varName'] = varName;
})();
Crescent Fresh의 의견을 해결하려면 : 시나리오에서 전역 변수를 완전히 제거하려면 개발자가 질문에서 가정 한 여러 가지를 변경해야합니다. 다음과 같이 보일 것입니다.
자바 스크립트 :
(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에서 제출 버튼을 클릭 할 때마다 경고가 발생합니다.
나는 모듈 패턴을 제안한다 .
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
첫째, 전역 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>
그러나 그럴 경우에는 아마도 객체 리터럴 또는 인스턴스화 된 객체를 사용하고 일반적인 OO 방식으로 진행해야합니다 (원하는 경우 모듈 패턴을 사용할 수 있음).
때로는 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);
}
}
이를 수행하는 다른 방법은 개체를 만든 다음 여기에 메서드를 추가하는 것입니다.
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();
};
})();
인라인 핸들러를 피하십시오.
클로저를 사용하면 중소 규모 프로젝트에 적합 할 수 있습니다. 그러나 큰 프로젝트의 경우 코드를 모듈로 분할하고 다른 파일에 저장할 수 있습니다.
따라서 문제를 해결하기 위해 jQuery Secret 플러그인 을 작성했습니다 .
이 플러그인을 사용하는 경우 코드는 다음과 같습니다.
자바 스크립트 :
// 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를 열면 전역이 전혀 없으며 funciton도 없습니다. :)
For full documentation, please see here.
For a demo page, please see this.
Source code on GitHub.
Use closures. Something like this gives you a scope 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
})();
For "securing" induvidual global variables:
function gInitUploadCount() {
var uploadCount = 0;
gGetUploadCount = function () {
return uploadCount;
}
gAddUploadCount= function () {
uploadCount +=1;
}
}
gInitUploadCount();
gAddUploadCount();
console.log("Upload counter = "+gGetUploadCount());
I'm a novice to JS, currently using this in one project. (i apreciate any comment and criticism)
참고URL : https://stackoverflow.com/questions/1841916/how-to-avoid-global-variables-in-javascript
'program tip' 카테고리의 다른 글
MVVM 라우팅 및 릴레이 명령 (0) | 2020.10.19 |
---|---|
유형을 지정하지 않고 Java Enum을 어떻게 참조 할 수 있습니까? (0) | 2020.10.19 |
람다를 받아들이는 함수를 선언하는 방법은 무엇입니까? (0) | 2020.10.19 |
프로덕션 코드에서 console.log를 제거해야합니까? (0) | 2020.10.19 |
Rails에서 JSON 형식의 404 오류를 반환해야합니다. (0) | 2020.10.19 |