본문으로 바로가기

유효범위의 효용

category JavaScript/Core & 개념ㆍ용어 2016. 9. 29. 08:59

스코프




유효범위의 효용

아래 두개의 예제는 변수 i를 지역변수로 사용했을 때와 전역변수로 사용했을 때의 차이점을 보여준다. 

전역변수는각기 다른 로직에서 사용하는 같은 이름의 변수값을 변경시켜서 의도하지 않은 문제를 발생시킨다.


function scope() {
var i = 0; // 지역변수
}

for(var i = 0; i < 5; i++) {
scope();
console.log(i);
}
// 의도한 결과대로 0,1,2,3,4를 출력



function scope2() {
j = 0; // 전역변수
}

for(var j = 0; j < 5; j++) {
scope2();
console.log(j);
}
// 의도치 않은 무한 루프에 빠지게 된다.


같은 이름의 변수를 사용했지만 각각의 취지가 다를때 위와 같은 의도치 않은 결과를 초래하게 된다.


위와 같은 코드는 우리가 짧기 때문에 쉽게 인지할 수 있지만 만약에 많은 양의 코드를 가지고 있는 코드라면 문제점을 발견하는 것은 대단히 어려워지게 된다. 


바로 이러한 문제를 해결,완화하기 위해서 도입된 개념이 프로그래밍 역사에서 전역변수,지역변수인 유효범위인 것이다.

처음부터 이러한 유효범위가 있었던 것은 아니라서 변수에 대한 정의가 대단히 조심스러웠다고 한다.


다시 말해, 전역변수,지역변수는 서로 다른 디렉토리에 같은 file 이름이 존재해도 덮어쓰지 않는 것과 같은 맥락이다.




전역 변수를 불가피하게 사용하는 경우

불가피하게 전역변수를 사용해야 하는 경우는 하나의 객체를 전역변수로 만들고 객체의 속성으로 변수를 관리하는 방법을 사용한다. 이것은 더글라스 크락포드가 고안한 방법(패턴)이다.

var MYAPP = {} // 전역변수 하나만 사용한다.
MYAPP.calculator = {
'left' : null, // 아래의 left, right 와 동일
'right' : null
}
MYAPP.coordinate = {
'left' : null,
'right' : null
}

MYAPP.calculator.left = 10;
MYAPP.calculator.right = 20;
function sum(){
return MYAPP.calculator.left + MYAPP.calculator.right;
}
console.log(sum());


전역변수 객체의 소속으로 생성하게 되면 변수의 이름이 충돌할 위험성은 현저히 낮아지게 된다.



전역변수 하나 자체도 사용하고 싶지 않은 경우에는 다음과 같은 방법을 사용한다.

(function(){ // MYAPP은 이 함수 내에서 선언된 지역변수가 된다.

var MYAPP = {} // 지역변수
MYAPP.calculator = {
'left' : null, // 아래의 left, right 와 동일
'right' : null
}
MYAPP.coordinate = {
'left' : null,
'right' : null
}

MYAPP.calculator.left = 10;
MYAPP.calculator.right = 20;
function sum(){
return MYAPP.calculator.left + MYAPP.calculator.right;
}
console.log(sum());
})();


전역변수를 사용하고 싶지 않다면 위와 같이 익명함수를 호출함으로서 사용가능하며, 이러한 방법이 로직을 모듈화하는 일반적인 방법이다. 


jQuery 라이브러리도 이러한 형태를 취한다.





정적인 유효범위

자바스크립트는 함수가 선언(정의)된 시점에서의 유효범위를 갖는다. 

이러한 유효범위의 방식을 정적 유효범위(static scoping), 혹은 렉시컬(lexical scoping)이라고 한다. (어휘적 유효범위)


렉시컬은 클로저와 관련된 개념이다.

var i = 5; // 전역변수

function a(){
var i = 10; // 지역변수
b();
}

function b(){
console.log(i); // 값을 예상해 보자.
}

a();


console.log(i) 의 i의 값은 처음에 b함수 내부의 i를 찾는다. i가 존재하지 않는다면 전역변수를 찾게 되는데

b()를 호출한 시점에서의 a 함수 내부의 i를 참조하는 것일까..

아니면 console.log(i)가 있는 b 함수가 선언된 시점에서의 전역변수 i를 참조하는 것일까??


결과는 전역변수의 5를 찾게된다. 


그 이유는 내부 i가 없다면 b 함수가 선언,정의된 시점에서 전역변수를 찾게 되는 것이지 호출한 시점에서... 위 코드에서는 b()가 담겨져 있는 a함수 선언에서의 내부 변수 i가 사용되는 것이 아니다.

즉, 함수가 사용,호출될 때가 아니라 정의,선언될 때 변수를 참조하게 된다.


이러한 것을 정적 유효범위, 렉시컬(어휘적 유효범위)이라고 한다.

b()호출은 누구에게서(어느 함수에서 호출될지) 사용될지 알 수 없다. 

a 함수내부에서 사용될 수도 있고 또 다른 함수 내부에서 호출될 수도 있다.

사용되는 대상에 따라서 대상이 갖고 있는 변수에 접근할 수 있는 코드라면 동적인 유효범위가 되지만 b 함수가 정의된 시점에서의 변수를 참조하고 있다면 누가 사용하든지 같은 결과를 가지기 때문에 이를 정적 유효범위,렉시컬이라고 부른다.


간결한 코드에서는 큰 문제가 발생하지 않을 수 있지만 코드량이 늘어나는 과정에서 이해할 수 없는 문제들에 대해 봉착하게 되었을때


그런 문제들이 이러한 유효범위와 관련된 경우가 흔히 발생되므로 이러한 개념에 대해 꼭 이해하고 넘어가도록 하자.



Jaehee's WebClub