Javascript - 변수관리 특징 및 스코프체인에 대하여

November 18, 2017

javascript는 다른 언어들과 상이한 점들을 특징으로 가지고 있는데요, javascript에서의 유효범위(scope)는 변수와 매개변수의 접근성과 생존 기간을 제어하는 범위를 말합니다.

1. javascript의 변수 관리

javasciprt에서 변수를 관리하는 규칙은 다음과 같이 세가지로 정리할 수 있습니다.

    1. 함수단위의 변수 관리
    1. 실행시의 변수 관리는 렉시컬 영역을 기준으로
    1. 실행시의 변수 검색은 스코프 체인을 이용

앞의 세가지를 저의 기준으로 설명 드리고자 합니다.

*)이 내용은 자바스크립트 객체지향 프로그래밍(위키북스) 및 더글라스크락포드의 > 자바스크립트 핵심 가이드 (한빛미디어, O'REILLY)를 참고하여 정리했습니다.

1) 함수단위의 변수 관리

앞서 말씀드린 것처럼, javascript는 함수 단위로 변수를 관리합니다. 이는 다른 언어들과 약간 상이한 특색을 보입니다. 예제를 보며 설명 드리겠습니다.

var a = 1;
function f () {
    if(true) {
        var c = 2;
    }
    
    console.log(c); //2가 출력
    
    return c;  
}

보시면, 다른 언어에서는 if문 안에서 변수 c를 선언했기 때문에, 반환되는 c의 값을 찾지 못하고 에러가 발생합니다. 그러나, javascript에서는 콘솔창에 2가 출력됩니다. 이유는 javascript에서의 변수 관리는 if, for문 등의 블럭단위로 유효범위가 설정되어 있는것이 아닌, 함수 단위로 유효범위(scope)가 설정되어 있기 때문입니다.

그렇기 때문에, if 블럭안의 변수 c는 함수 전체에서 관리될 수 있으므로, 콘솔에 2의 결과가 출력됩니다. 이뿐만 아니라, javascript내의 변수의 특성을 잠깐 살펴보면,

  • 첫번째, javascript에서의 변수는 중복이 가능하며, 변수를 사용하는 곳에서 가장 가까운 변수를 참조합니다.
function c () {
    var a = "첫번째 a";
    var a = "두번째 a";
    
    return a;
}
 
console.log(c()); //"두번째 a"가 출력

위의 예제에서 처럼, 변수a는 같은 변수명을 사용하고 있지만, 반환되는 지점에서 가장 가까운 "두번째 a"라는 값을 가지고 있는 변수 a를 반환해 줍니다.

  • 두번째, "var"키워드가 없는 경우에는 전역 변수로 설정됩니다. 변수를 생성하는 var키워드가 없는 경우에는, 전역 변수로 설정됩니다. 이때문에, 꼭 함수내에서 사용하고자 하는 경우에는 var 키워드를 포함해 주어야 함수 내에서 정상적으로 동작 가능합니다. 전역 변수일 경우에는 다른 곳에서도 사용할 위험이 있습니다.

2) 실행시의 변수관리는 렉시컬(lexical) 영역을 기준으로

렉시컬(lexical)은 영문으로 "단위, 어휘와 관련 있다"라는 의미를 지닙니다. javascript에서는 렉시컬 특성을 프로그램 "코드"와 관련있다고 생각할 수 있습니다. 즉, 함수를 실행 단계가 아닌 정의단계에서 유효범위를 설정하고 있습니다.

var x = "global";
function f () {
    alert(x);            //undefined 출력
    
    var x = "local";    //지역변수 "local" 선언
    
    alert(x);            //"local" 출력
}

위와 같은 예제가 있습니다. 얼핏 보면 실행중에 "global" 선언된 변수 x로 인해서 함수 f안의 첫번째 alert은 global을 출력할 것으로 보이지만, 실제로는 undeinfed 가 출력됩니다. 이는 변수의 유효범위는 실행 중에 설정되는 것이 아닌, 정의될때의 함수 단위로 설정이 되기 때문입니다.

위의 변수에 대한 스코프 객체를 살펴보겠습니다. 변수의 스코프 객체

위의 예제를 실행하게 되면, 첫번재로 전역 레벨에서 파싱이 일어나면서, "global"값을 가진 변수 x와 함수 변수인 f가 정의됩니다. 이후에 f를 실행하게 되면, 함수 f가 호출되면서, 함수 f레벨의 파싱이 이루어 집니다. 이 결과로 함수 내부에 있는 x가 변수 스코프 객체에 정의됩니다.

실행 환경에서 x="global"이 실행되고 난 후에, f()에 따라서 함수 f가 파싱됩니다. 파싱될 때의 함수 f에서 첫번째 alert(x)를 위한 x변수를 찾아야 하나, 정의된 시점에서 변수가 유효범위를 가지고 있기 때문에, x가 없는 관계로 첫번째 alert(x)는 "undefined" 가 출력됩니다.

이후, x를 "local"로 선언해 주고 나서야, 두번째 alert(x)는 x값을 찾고, local 을 출력하게 됩니다.비슷한 문제로, 다음과 같은 경우도 살펴보겠습니다.

function f1 () {
    var a = 1;
    f2 ();
}
 
function f2 () {
    return a;
}
 
f1(); //a is not defined error
변수의 스코프 객체

위의 예제를 변수 스코프로 살펴봅니다. 보시면 전역 레벨에는 함수 f1을 정의하고, f1함수 내에서는 f2를 정의하면서, 순차적으로 레벨단계에서의 파싱이 일어납니다.

이때, 실행환경에서 f1 함수를 호출하고, f1함수는 내부의 f2함수를 호출하지만, f2레벨에서 정의될때의 환경에서는 a값이란 변수가 설정되어 있지 않습니다. 또한, 전역 객체에서도 변수 a값은 찾을 수 없습니다. 이로 인해서, a값을 찾지 못하므로, a is not defined에러가 출력됩니다.

이처럼, javascript내에서의 변수 스코프는 실행 환경 내에서 설정이 되는 것이 아닌, 렉시컬한 환경, 즉 함수가 정의될때의 환경을 기준으로 변수의 유효범위가 설정됩니다.

3) 실행시의 변수 검색은 스코프 체인을 이용(scope chain)

function abcFunc (a,b) {
        var c = a + b;
                
        return c;
}
    
abcFunc(1,2);

위의 예제처럼 abcFunc가 실행되면, 다음과 같은 실행 문맥이 형성됩니다.

스코프체인 설명

실행 문맥은 다음과 같은 순서로 생성됩니다.

  • 1.활성화 객체 생성
  • 2.arguments(참조 변수) 객체 생성
  • 3.유효범위 (스코프 체인) 객체 생성
  • 4.변수생성
  • 5.this 객체 생성
  • 6.실행

arguments객체는 인자값으로 보내는 1,2 등이 배열형태로 제공되는 객체입니다. 이부분에서 스코프 체인을 보시면, 스코프체인 객체의 컬렉션에는 유효범위에 대한 정보가 생성됩니다. abcFunc에서의 실행문맥에서 스코프 체인은 0번째에 전역변수 객체 정보를 생성합니다. 이러한 스코프체인을 통해서 함수가 중첩함수인 경우 상위함수의 유효범위까지 흡수함으로써 변수 스코프 객체간의 부모, 자식 관계가 생성됩니다.

다음과 같은 중첨합수의 예제를 보겠습니다.

var x = 1;
function outer () {
    var y = 2;
    function inner () {
        var z = 3;
        var a = x;
    }
}

이에 대한 변수 스코프 및 스코프 체인 객체는 다음과 같습니다.

스코프체인 설명

만약 이 함수에서 inner 함수에 x=a;코드가 실행될때를 가정합니다.

이코드가 실행될 경우 inner 스코프객체에서는 변수 x가 검색되는데, 처음은 정의하고 있는 함수의 변수 스코프에서 먼저 검색됩니다. (inner 변수 스코프 객체) 만약 정의된 함수의 변수 스코프에서 x가 검색되지 못하면, 해당 함수를 포함하고 있는 상위 함수인 outer의 변수 스코프에서 검색되며, 이때도 검색이 되지 않을 경우 전역 변수 스코프에서 x가 검색됩니다.

이처럼, 하위함수에서 상위함수로의 부모/자식 관계가 정의된 것을 스코프 체인이라고 합니다.

세가지 특징을 통해서 javascript의 변수 관리를 살펴보았습니다.
나름 책과 구글링을 통해 정리한 내용이지만, 이해하기 어려운 부분도 있으실 것이라고 생각합니다.

중요한 것은, javascript의 변수 관리는 함수단위 이며, 정의된 환경에서 외부 및 내부 함수의 스코프체인을 통해 변수를 검색한다는 것을 기억하시면 될 것 같습니다. 다음은 javascript에서 중요한 클로져(Closure)를 살펴볼까 합니다.

출처: http://lazydev.tistory.com/41?category=593988 [Kern]

...