본문 바로가기

Javascript

자바스크립트 scoping에 대해

728x90
스코핑 (Scoping)에 관련된 여러가지 정의

Scoping 이란?

프로그램의 변수를 제어하는 방법으로, 자바스크립트 엔진에 의해 구성, 접근된다

"변수는 어디에 생기나요?" "특정 변수에 엑세스 할 수 있는 위치, 할 수 없는 위치는 어디인가요?"

이런 질문에 대한 것이 스코핑입니다. 즉 범위에 대한 이야기를 하는 것입니다.

lexical scoping 이란?

코드의 함수, 블록들의 위치에 의해 Scoping 범위가 조절된다

라는 것이 lexical scoping의 정의입니다.

예를 들어 자식 함수는 부모 함수에서 선언된 변수에 접근할 수 있습니다.

Scope 란?

특정 변수가 선언되었을 때 가지는 공간이나 환경을 말한다.
각각 global, function, block scope가 있다.

Scope of a variable

특정 변수가 접근할 수 있는 코드의 전체 영역

사실 Scope와 Scope of a variable 의 의미는 비슷해보일 수 있습니다. 하지만 미묘한 차이를 가집니다.

아래에서 코드를 보면서 이해한다면 정확한 차이점을 알 수 있을것입니다.

 

Scope 범위

1. Global Scope

const me = 'overaction'
const job = 'student'
const food = 'apple'

글로벌 스코프는 프로그램의 모든 함수, 모든 블록 등 어디에서나 접근할 수 있는 범위입니다.

 

2. Function Scope

function calcAge(birthYear) {

    const now = 2037;
    const age = now - birthYear;
    return age;
    
 }
 
 console.log(now); // 에러

각 함수는 일정한 범위를 만들게 되며, 해당 함수 범위 내부에 선언된 변수는

해당 함수 내에서만 접근할 수 있는 것이 함수 스코프 입니다.

* local scope 라고도 부릅니다

 

위의 코드에서 now에 대해 console.log 를 사용해서 접근하려고 하면 "Reference(참조) 에러" 가 발생하게 됩니다.

즉 자바스크립트가 함수 외부의 글로벌 스코프에서 now라는 변수를 찾으려고 시도했지만 찾을 수 없었기 때문입니다.

 

3. Block Scope (ES6)

if(year >= 2000 && year <= 2050) {
    const abc = true;
    const food = 'apple'
}

console.log(abc);  // 에러

자바스크립트가 ES6로 넘어오면서, 블록 또한 블록 스코프를 만들게 되었습니다.

중괄호 사이에 있는 것을 의미하는데 해당 블록 내에서만 접근할 수 있습니다. (if문 또는 for문)

중요한 차이점은, const 또는 let 으로 선언한 변수만이 블록 스코프를 가지게 된다는 것입니다.

 

const myName = 'overaction';

function first() {
    const age = 20;

    if(age>=20) {
        const abc = 3;
        var def = false;
    }

    function second() {
        const food = "apple";

        console.log(`${myName} is a ${age}-old like ${food}`);
    }
}

 

 

 

위의 코드와 그림에서처럼, 반드시 부모 scope를 자식 socpe가 접근할 수 있지만, (lookup)

반대로 부모 scope가 자식 scope에 접근할 수는 없습니다. (lookdown)

 

    if(age>=20) {
        const abc = 3;
        var def = false;
    }

그리고 이 블록 스코프를 살펴보면, 앞서 말했듯이 const 나 let 으로 선언된 변수는 block scope 지만,

var 로 선언된 def 변수는 그보다 부모 scope인 함수 scope가 될 것입니다.

 

따라서 def 는 second 함수에서 접근이 가능하지만, abc 변수는 접근이 불가능합니다.

 

 

Scope chain vs Call stack

스코프 체인과 call stack 간의 관계를 한번 살펴보겠습니다.

스코프 체인은 자신이 속한 스코프에서 변수를 찾고, 없다면 상위 스코프에서 계속해서 찾아나가는 것입니다.

위의 사진에서 first, second, third 함수가 차례대로 호출될 때, call stack에 들어가게 되는 실행 컨텍스트 순서는 다음과 같습니다. 각 실행 컨텍스트마다 variable environment(변수 환경)이 각각 있는걸 볼 수 있습니다.

 

다음으로 전역 스코프와 first() 함수에 대한 스코프를 살펴보면,

앞에서 배웠듯이 first 스코프에는 부모 스코프인 전역 스코프의 변수 환경이 추가됩니다.

중요한 점은, 함수의 호출 순서는 스코프 체인에 관계가 없다는 것입니다.

그다음 second 함수의 스코프는 다음과 같이 나올 것입니다.

 

그런데 second함수에서 그보다 아래에 있는 third 함수를 호출하는 것이 보입니다. 어떻게 이런 것이 가능한 것일까요?

바로 third 함수가 global scope에 있기 때문입니다. 따라서 어디에서든 실행할 수 있습니다.

 

마지막으로 third 함수에서 d c b a 에 대해 접근하려고 합니다. 과연 어떻게 될까요?

1. d 는 third 함수의 변수 환경에 있으니 문제가 되지 않습니다.

2. c 의 경우 어떤가요? second 함수 내에서 선언되었기 때문에 third 함수에서 접근할 수 없고, 참조 에러가 발생하게 됩니다. 비록 second 함수에서 third 함수를 실행했더라도 전혀 영향이 없습니다.

앞에서 말했던 것처럼, 함수의 호출 순서는 scope chain에 영향을 주지 않습니다.

3. b 도 마찬가지로 찾을 수 없습니다.

4. a 는 전역 스코프에 있기 때문에 접근할 수 있습니다.

 

 

요약

1. Scoping"변수는 어디에 생기나요?" "특정 변수에 엑세스 할 수 있는 위치, 할 수 없는 위치는 어디인가요?" 를 질문하는 것입니다.

 

2. Scope의 3가지 종류로, 글로벌 스코프, 함수 스코프, 블록 스코프가 있습니다.

다만 블록 스코프의 경우 const, let 변수만 가능합니다. var 변수는 가장 가까운 함수의 스코프로 들어가게 됩니다.

 

3. lexical scoping 이란, 우리가 변수에 접근하기 위한 규칙은 함수와 블록이 작성된 위치에 따라 달라진다는 것입니다.

 

4. 모든 스코프는 항상 해당 스코프 바깥쪽에 있는 스코프에 있는 변수에 접근할 수 있습니다. 이것을 scope chain 이라고 부릅니다.

 

5. 만약 특정 변수가 현재 scope에 없을 경우, 찾을 때까지 스코프 체인을 탐색하게 됩니다. => variable lookup

 

6. 스코프 체인의 방향성은 단방향입니다. 따라서 해당 스코프보다 안쪽에 있는 스코프의 변수에는 절대 접근할 수 없습니다. 반드시 바깥쪽 범위의 스코프에 접근할 수 있습니다.

 

7. 함수의 호출 순서는 스코프 체인에 영향을 주지 않습니다.

728x90