티스토리 뷰


JS


유효범위(Scope)



변수나 함수는 이름을 부여하여 의미를 갖게 됩니다. 만약 이름이 없다면, 변수나 함수는 그저 하나의 메모리 주소에 지나지 않습니다. 그래서 "이름:값"의 대응표를 만들어 사용합니다. 이 대응표의 이름을 가지고 값을 저장하고 가져와 사용합니다. 이 대응표의 충돌을 막기 위해 유효범위(Scope)라는 규칙을 만들어 사용하게 됩니다.


자바스크립트 또한 스코프 규칙을 가지고 있는데, 자바스크립트(ES6)는 함수 레벨, 블록 레벨의 렉시컬 스코프 규칙을 따릅니다.



1. 함수 레벨 스코프 (Function Level Scope)

자바스크립트에서 var 키워드로 선언된 변수나, 함수 선언식으로 만들어진 함수는 함수 레벨 스코프를 갖습니다. 즉 함수 내부 전체에서 유효한 값을 가지게 됩니다.

function functionLevelScrop() {
    if (true) {
        var myScope = "function level scope";
    }
    console.log(myScope);
}

functionLevelScrop();

- 코드 1 -


코드 1 실행 결과코드 1 실행 결과


코드 1 실행 결과에서 보시는 것 처럼 if 안에서 생성한 myScope가 console.log로 출력 되는 것을 확인 할 수 있습니다.

만약 var myScope가 블록 레벨 스코프라면, if문이 끝날 때, myScope가 없어지고, console.log에서 에러가 발생할 것입니다. 하지만 var myScope는 함수 레벨 스코프이기 때문에 functionLevelScope 함수 내부 어디에서든 사용할 수 있습니다.



2. 블록 레벨 스코프 (Block Level Scope)

ES6에서 let, const 키워드는 블록 레벨 스코프 변수를 만들어 줍니다. ([자바스크립트] ES6(ECMA Script 6) - let, const 참고)

function blockLevelScrop() {
    if (true) {
        let myScope = "block level scope";
        console.log(myScope);
    }
    console.log(myScope);
}

blockLevelScrop();

- 코드 2 -


코드 2 실행 결과코드 2 실행 결과


let myScope를 if 문 안에 선언하였기 때문에, if 문 안 console.log로 값이 출력이 되지만, if 문 밖에서는 console.log로 출력 하자 에러가 나타나는 것을 확인 할 수 있습니다.


3. 렉시컬 스코프 (Lexical Scope)

렉시컬 스코프는 정적 스코프(static scope) 라고도 합니다.

렉시컬 스코프는 소스코드가 작성된 그 문맥에서 결정됩니다. 예를 들어보겠습니다.

var x = "global";

function func1 () {
    var x = 'local';
    func2();
}

function func2() {
    console.log(x);
}

func1();
func2();

- 코드 3 -


코드 3 실행 결과코드 3 실행 결과


var x를 window 영역에서 global로 선언되어 있습니다.

func2()의 결과는 예상하신 대로 global이 출력됩니다. 그렇다면 func1()의 결과는 왜 global일 까요? 이유는 렉시컬 스코프에 있습니다.

x를 func1에서 local로 변경한 후, func2를 실행하면 func2는 x를 출력하게 됩니다. func2에서의 x는, 코드가 작성된 문맥에서 존재하지 않기 때문에, 상위 스코프에서 x를 찾게 되고(4. 중첩 스코프 참고), 결국 window 영역의 x를 출력하게 됩니다.


4. 중첩 스코프 (Scope Chain)

자바스크립트의 스코프는 ECMAScript 언어 명세에서 렉시컬 환경(Lexical environment)과 환경 레코드(Environment recode)라는 개념으로 되어 있습니다.


환경 레코드는 위에서 말씀 드렸던 것 처럼 "이름:값"의 대응표라고 할 수 있습니다.

렉시컬 환경은 이 환경 레코드와 상위 렉시컬 환경에 대한 참조로 이루어져 있습니다.


중첩 스코프 예제중첩 스코프 예제


현재 렉시컬 환경의 대응표(환경 레코드)에서 변수를 찾아보고, 없다면 상위 렉시컬 환경을 참조하여 찾아보는 방식으로 중첩 스코프가 동작합니다.

해당하는 이름을 찾거나 상위 렉시컬 환경 레코드가 null일 될 경우 중첩 스코프 탐색이 중단됩니다.



호이스팅(Hoisting)


호이스팅이란 변수의 정의가 그 스코프에 따라 선언과 할당으로 분리되는 것을 의미합니다. 선언 부분이 해당 스코프의 최상위로 변경됩니다.


예를 한가지 들어보겠습니다.

function hoisting() {
    hoistingText = "hoisting";
    var hoistingText;
    console.log(hoistingText);
}
hoisting();

- 코드 4-1 -


코드 4-1 실행 결과코드 4-1 실행 결과


코드 4-1의 실행 결과는 호이스팅에 의해 hoisting이 정상적으로 출력 됩니다. 코드 4-1가 호이스팅에 다시 해석된 코드는 코드 4-2와 같습니다.

function hoisting() {
    var hoistingText;
    hoistingText = "hoisting";
    console.log(hoistingText);
}
hoisting();

- 코드 4-2 -


한가지 예를 더 들어 보겠습니다.

function hoisting() {
    console.log("1 : " + hoistingText );
    var hoistingText = "hoisting";
    console.log("2 : " + hoistingText);
}
hoisting();

- 코드 4-3 -


코드 4-3 실행 결과코드 4-3 실행 결과


코드 4-3의 실행 결과는 1번째 로그는 undefined, 2번째 로그는 hoisting이 출력 됩니다. 호이스팅된 코드(코트 4-4)로 설명 드리겠습니다.

function hoisting() {
    var hoistingText;
    console.log("1 : " + hoistingText );
    hoistingText = "hoisting";
    console.log("2 : " + hoistingText);
}
hoisting();

- 코드 4-4 -


호이스팅으로 var hoistingText의 선언이 최상위로 변경되기 때문에, 1번째 로그가 출력 될 때는 undefined가 2번째 로그는 hoisting이 할당되어, hoisting이 출력되게 됩니다.




(참조 http://meetup.toast.com/posts/86)

댓글
댓글쓰기 폼