말장난으로 시작한 제목과는 달리 역설적으로 JavaScript에선 global space를 더럽히는 일은 죄악처럼 여겨진다. 특히나 통제할 수 없는 다른 사람이 작성한 JavaScript 코드가 섞여들어 갈 가능성이 큰 Client-Side JavaScript에선 더욱 그러하다.
그래서 뜻하지 않는 이런 실수를 저지르지 않으려면 변수가 global 해지는 경우를 정확하게 알고 있어야 하는데, 그 예로 다음과 같은 몇 가지 경우가 있다.
var로 시작되지 않는 변수 선언.
잘 알려진 내용으로 var
없이 선언된 변수는 global object의 properties에 붙게 된다.
참고로, 이렇게 만들어진 property는 var
선언문으로 생성된 property와는 다르게 다시 나중에 delete
로 지울 수 있다.
var x = 1; // A properly declared global variable, nondeletable.y = 2; // Creates a deletable property of the global object.thisz = 3; // This does the same thing.delete x; // => false: variable not deleteddelete y; // => true: variable deleteddelete thisz; // => true: variable deleted
다음은 JavaScript의 delete operator 작동 원리에 대한 자세히 설명해놓은 글. – Perfection Kills – Understanding delete
function 밖에서 선언된 변수.
JavaScript에선 다른 languages와는 다르게 block scope이 아닌, function scope 규칙을 따른다. 또 한 가지 재미있는 사실은 function 안에 있는 변수는 초깃값으로 선언되기 이전이라도 function body 전체 어디서든 인식되고 사용할 수 있게 된다. 이것을 보통 끌어올림(hoisting) 현상이라고 부르는데, function 안 변수 선언문은 function body의 최상위로 끌어올려 지는 것과 같다. 그래서 혹시라도 global 변수와 이름이 같은 변수를 function 안에도 선언해서 허용할 때 다음과 같은 예기치 않은 부작용을 가져올 수 있어서 주의해야 한다.
var scope = "global";console.logscope; // Prints "undefined", not "global"var scope = "local"; // Variable initialized here, but defined everywhereconsole.logscope; // Prints "local"
그래서 변수 선언은 미리 function body 맨 위에다 해주는 습관을 들으면 예기치 않은 혼란을 피할 수 있다.
또, function() constructor를 써서 생성된 function은 항상 최상위 레벨의 global function으로서 자기만의 private scope을 지닌 또 하나의 eval()
버전으로 생각할 수 있는데, 잘 쓰이지는 않지만, 다음과 같은 예에서 이런 현상을 잘 보여준다.
var scope = "global";var scope = "local";return "return scope"; // Does not capture the local scope!// This line returns "global" because the function returned by the// Function() constructor does not use the local scope.constructFunction; // => "global"
function 이름
function 이름은 global 오브젝트의 method로 다음과 같은 scope을 생성할 수 있다.
//==window.aGlobal();//param is only accessible in this functionvar scopedToFunction =//can't be accessed outside of this functionnested : 3 //accessible by: scopedToFunction.nested;anotherGlobal =//global because there's no `var`;
그래서 JavaScript에선 function을 global namespace를 더럽히지 않으려는 하나의 임시 namespace 도구로 많이 사용된다.
한 가지 주의할 것은 일반 ECMAScript 3에선 this
가 global 오브젝트를 가리키지만, ECMAScript 5 strict mode에선 undefined
를 돌려준다.
with 선언문 안에서 사용된 변수 선언
먼저 with
선언문은 임시로 scope chain을 확장하려고 할 때 사용되는데, JavaScript가 코드를 최적화하기 어렵고 strict mode에선 사용이 금지되어 있으며 다른 코드로 쉽게 대체할 수 있기 때문에 사용할 일은 거의 없겠지만, 혹시라도 다음과 같은 코드를 짜게 된다면 문제가 발생할 수 있다.
witho x = 1;
여기서 만약에 o 오브젝트에 x property가 정의되어 있다면 1의 값을 지정하겠지만, 그렇지 않을 때는 with
선언문 없이 그냥 x = 1;
과 같아서 x라는 이름의 local 혹은 global 변수의 값을 지정하거나 global 오브젝트의 새로운 property를 생성하게 되므로 주의해야 한다.
변수 선언 중 불필요한 꼼수 부림
가끔가다 한꺼번에 여러 변수에 어떤 하나의 값을 지정하려 할 때 다음과 같은 꼼수가 쓰이는 것을 보게 된다.
var a = b = 1;
하지만, 여기엔 예기치 않은 커다란 부작용이 있는데, 실제로는 결과적으로 다음과 같이 의도하지 않은 global 변수를 선언하게 된다.
b = 1;var a = b;
글자 몇 개 줄이려다 큰 실수를 범하지 말고 다음과 같이 의도한 대로 써줘야 한다.
var a = 1 b = 1;
let keyword
참고적인 얘기로 JavaScript엔 없는 block scope을 만회하려고 Mozilla의 JavaScript extension인 “Javascript 1.7″에선 비표준인 let keyword가 소개되었는데, 이놈은 새로운 block-scope 변수를 선언할 때 다음과 같이 사용될 수 있다.
var a = 4;let a = 3alerta; // 3alerta; // 4
아직 ECMAScript 표준에는 채택되지 않아서 일반 JavaScript 코딩에는 사용될 수 없다.
흔히 컴퓨터 프로그래밍은 사람이 발명한 것 중에도 가장 복잡한 것 중 하나로 꼽는데, 여기에다 JavaScript는 다른 언어보다도 코딩 자유도가 높고 또 이와 맞물려 예기치 않은 결과를 가져올 수 있는 요소도 곳곳에 숨어 있다. 그래서 예측하기 쉬운 코드를 짜도록 노력한다면 쉽게 눈치챌 수 있는 명확한 에러는 줄일 수 있을 것이다. 이와 관련해서 Douglas Crockford씨가 TXJS 2011에서 한 개막 연설은 귀담아서 들어야 할 내용이 담겨 있다.