[TIL] Scope Chain 이해하기 (JavaScript)
TIL/[위코드] 풀스택 부트캠프 2기

[TIL] Scope Chain 이해하기 (JavaScript)

스코프란?

  • 변수(식별자)의 유효 범위를 뜻한다. 스코프(Scope)는 변수가 어디서, 어떻게 선언되었는지에 따라 그 변수의 유효한 범위가 결정되는 규칙이기도 하며, 자바스크립트에서는 이 규칙에 따라 참조의 대상이 되는 변수를 탐색해나간다. (→ Scope Chain)
    • 전역 스코프(Global Scope): 코드 어디에서든지 참조할 수 있다. (→ 전역 변수)
    • 지역 스코프(Local Scope or Function-level Scope): 함수 스코프 또는 코드 블록(if-else, for, while, try-catch 등)이 만든 블록 스코프로, 자기 자신 또는 그 하위 함수/블록에서만 참조할 수 있다. (→ 지역 변수)
var x = 'global scope';

function foo() {
  var x = 'function scope';
  console.log(x);
}

foo();           // "function scope"
console.log(x);  // "global scope"

예를 들어, 위 예제에서 전역 스코프에 선언된 변수 x는 코드 어디에서든지 참조할 수 있다. 하지만, 함수 foo 내에서 선언된 변수 x는 함수 foo 내부에서만 참조할 수 있고 함수 외부에서는 참조할 수 없다.

 

만약 스코프가 없다면 어떻게 될까?

스코프가 없다면, 같은 이름의 변수끼리 충돌을 일으키게 되므로 프로그램 전체에서 같은 이름을 갖는 변수를 하나밖에 사용할 수 없다.
디렉터리가 없는 컴퓨터를 생각해보자. 만일 디렉터리가 없다면, 같은 이름을 갖는 파일을 하나밖에 만들 수 없을 것이다. 이와 마찬가지로 스코프는 변수 이름 간의 충돌을 미연에 방지하는 역할도 수행한다.


스코프 체인이란?

자바스크립트 엔진은 변수(식별자)를 찾을 때 일단 자신이 속한 스코프에서부터 찾아보고, 그 스코프에 찾는 변수가 없으면 상위 스코프를 타고 올라가며 차례차례 찾아 나간다. 이를 스코프 체인(Scope Chain)이라고 하며, 스코프가 중첩되어있는 모든 상황에서 발생한다.

  • [주의] 단, 상위 스코프에서 하위 스코프에 선언된 변수로는 접근할 수 없다.

스코프(Scope)는 위 그림처럼 중첩된 계층 구조(Chain)로 형성된다. 어떤 함수로도 둘러싸여 있지 않은 가장 최상위 스코프를 전역 스코프(Global Scope)라고 부르며, 그 하위엔 함수 생성을 기준으로 지역 스코프가 층층이 형성된다. 위 그림에서는 가장 최상위에 전역 스코프가 존재하고, 그 하위에는 Person 함수 스코프가 존재하며, Person 함수 스코프 내부에 displayName이라는 함수의 스코프가 존재한다.

[상위] Global Scope ← Person Function Scope ← displayName Function Scope [하위]

이런 Scope Chain에서 거듭 강조할만한 특징은 하위 스코프에서 상위 스코프의 정보로는 접근할 수 있지만, 상위 스코프에서 하위 스코프의 내부 정보로는 접근할 수 없다는 것이다. 실행문의 위치를 기준으로 하위 스코프부터 시작하여 원하는 값을 찾을 때까지 상위 스코프를 타고 올라가며 탐색해나가는 것이 Scope Chain의 가장 기본적인 작동방식이다.


블록 스코프란?

블록 스코프(Block Scope)는 원래 C 같은 원시 언어에는 존재했지만,
초기 자바스크립트에서는 존재하지 않던 개념이었다.

초기 자바스크립트에서는 변수의 유효 범위가 함수의 유효 범위(Function Scope)만을 따랐지만, 현재의 자바스크립트(ES6 이후)에서는 변수의 유효 범위가 블록의 유효 범위인 블록 스코프를 따를 수 있도록 let과 const라는 키워드가 새롭게 도입되었고, 이로써 자바스크립트에서도 블록 스코프를 구현할 수 있게 되었다.

 

여기서 블록(Block)이란, 말 그대로 if-else, for 등의 명령에서 {중괄호 블록} 안에 작성되는 부분을 일컫는다. 이렇게 한 블록 안에서 let이나 const 키워드로 선언한 변수는 바깥 블록에서 접근할 수 없다는 것이 블록 스코프의 기본전제다. 대부분의 프로그래밍이 블록 스코프를 따르고 있었기 때문에 자바스크립트 역시 새로운 키워드(let, const)를 도입함으로써 블록 스코프를 가능케 만든 것이라고 보면 되겠다.


[참고] 자바스크립트에서 변수를 선언하는 세 가지 방식

  1. var: 함수 스코프를 따르며, 자신이 선언된 곳과 가장 가까운 함수를 유효 범위로 가진다.
    • var는 재선언과 재할당이 모두 가능 (ECMAScript 6의 등장 이후 더는 잘 쓰지 않는 편)
  2. let, const: 블록 스코프를 따르는데, 자신이 선언된 곳과 가장 가까운 블록을 유효 범위로 가진다.
    • let은 재할당만 가능, const는 재선언과 재할당이 모두 불가능 (ECMAScript 6에서 도입)
    • 일반적으로는 변수에 저장된 값이 변경되는 값이라면 let을, 저장만 하고 사용하는 값이라면 const를 이용해 선언한다.
var x = 0;
{
  var x = 1;
  console.log(x);  // 1
}
console.log(x);    // 1
let y = 0;
{
  let y = 1;
  console.log(y);  // 1
}
console.log(y);    // 0

var는 함수 스코프를 따르기 때문에 첫 번째로 선언된 x와 두 번째로 선언된 x 모두 전역 스코프에서 선언되었다고 볼 수 있다. 즉, 두 번째로 선언된 x가 같은 전역 스코프에 선언되어 있던 첫 번째 x를 덮어씌웠기 때문에 두 출력값이 모두 1이 된다. 반면, let은 블록 스코프를 따르기 때문에 첫 번째로 선언된 y와 두 번째로 선언된 y는 각기 다른 스코프에서 선언된 변수이다. 따라서 두 출력값은 각각의 스코프에서 참조한 y의 출력 결과를 나타낸다.


※ 참고 링크

poiemaweb.com/js-scope

leehwarang.github.io/2019/10/07/scope.html