2 minute read

자바스크립트에서 스코프란 변수에 접근할 수 있는 범위를 말한다. 일반적으로는 전역 스코프와 지역 스코프로 구분 된다.

전역 스코프

만일 변수가 모든 함수에 속하지 않고 블록({})에도 들어있지 않다면 그 변수를 전역 변수라고 한다. 전역 변수는 자바스크립트 내의 어떠한 스코프에서도 사용할 수 있다.

const globalVariable = "some value"; // 전역 변수

function hello() {
  console.log(globalVariable);
}

console.log(globalVariable);

변수 선언 방식이 var 밖에 없던 es5 이전에선 전역 변수의 사용은 최대한 자제하는 것이 상식이었다.

이 부분은 var의 선언적 특성 중 재선언이 가능하다는 특징 때문이다.

es6+ 이후에는 전역 변수를 그렇다고 자주 써야되냐고 한다면 그건 아니겠지만 재선언적 특징에 의한 위험도는 사라졌다고 봐도 된다.

지역 스코프

코드 내 특정 구역에서만 사용할 수 있는 변수를 지역변수라고 한다. 지역 변수는 2가지 레벨로 구분된다.

  1. 함수 스코프 레벨
  2. 블록 스코프 레벨

함수 스코프(Function Scope)

함수 내에서 변수를 선언하게 되면 그 함수의 범위 안에서만 이 변수에 접근할 수 있다. 함수 외부에서는 함수 내부에 있는 변수에 접근할 수 없다. 아래 예제를 보자.

function helloWorld() {
  const hello = "Hello World!";
  console.log(hello);
}
helloWorld(); // "Hello World!"
console.log(hello); // Uncaught ReferenceError: hello is not defined

위와 같이 함수 내에 변수를 선언하고 함수를 실행하면 정상적으로 지역변수를 참조하여 console.log를 실행하는 것을 볼 수 있다. 하지만 전역 스코프에서 함수 스코프 내의 지역변수를 접근하려고 하면 is not defined 에러를 반환한다.

블록 스코프(Block Scope)

블록 스코프의 특징은 함수(Function)이 아닌 if, for 등의 블록({})으로 이루어진 영역을 의미한다. 여기에 선언된 지역변수는 블록 스코프 레벨 변수라고 한다.

함수 스코프와 블록 스코프를 굳이 구분해서 설명하는 이유는 var, let, const가 함수 스코프와 블록 스코프에서 동작 방식이 다르기 때문이다.

일단은 아래 간단한 예제만 확인해보자.

if (true) {
  const hello = "Hello World!";
  console.log(hello); // "Hello World!"
}
console.log(hello); // Uncaught ReferenceError: hello is not defined

스코프 체인

다른 스코프 동작을 설명하기 전 우선 스코프 체인에 대한 이해가 필요하다.

자바스크립트는 코드 실행 시점에 Execution Context(줄여서 EC)가 생성되고, 그 시점에 다양한 행위들이 이뤄진다.

그리고 EC는 스코프 체인(줄여서 SC) 프로퍼티를 가지고 있다.

또한, SC에서는 중첩된 스코프의 변수, 함수 선언을 참조할 수 있는 Activation Object(AO), Global Object(GO) 리스트를 담고 있다.

스코프 체인은 아래와 같은 특성이 있기 때문에 헷갈려서는 안된다.

스코프 체인은 식별자 중에서 객체(전역 객체 제외)의 프로퍼티가 아닌 식별자, 즉 변수를 검색하는 메커니즘이다.
식별자 중에서 변수가 아닌 객체의 프로퍼티(물론 메소드도 포함된다)를 검색하는 메커니즘은 프로토타입 체인(Prototype Chain)이다.

중첩된 스코프에서 하위 스코프부터 상위 스코프(전역 스코프까지)까지 스코프 체인을 통해 검색하게 되는데 함수 또는 표현식 실행 중에 변수를 만나면 그 변수를 우선 현재의 AO에서 검색해보고 실패하면 순서대로 검색을 이어가 GO까지 도달하게 된다. 이것을 스코프 체인이라고 한다.

물론 최종적으로 GO까지 도달했음에도 검색에 실패한다면 Reference 에러를 반환한다.

어휘적 스코프(Lexical Scope)

lexical scope는 함수 스코프와 블록 스코프를 동작에 자연스레 포함되어 있다.

왜냐하면, lexical scope는 함수 혹은 변수가 선언된 위치를 기준으로 scope 영역을 정하는 방식이기 때문이다.

아래 간단한 예제를 보자.

var number = 1;

function a() {
  var number = 10;
  b();
}

function b() {
  console.log(number);
}

a();
b();

전역변수 number에 1이 선언되었고, 햠수 a의 지역변수로서 number에는 10이 선언되었다.

그리고 함수 a에서는 함수 b를 호출하고 있다.

자바스크립트는 스코프 체인에 의해 렉시컬 스코프를 파악하게 된다.

함수 a에 지역변수 number가 선언되었지만, 함수 b는 지역변수가 없다.

a함수에서 b함수를 호출한 시점에 상위 스코프가 정해지는 방식이 아니기 때문에 함수 b는 함수 a의 지역변수인 number를 상위 스코프로 정하는 게 아니라 전역변수의 number를 상위 스코프로 정하게 된다.

따라 a, b를 호출하면 둘다 10을 반환하게 된다.

참고로 함수의 호출 시점에 상위 스코프가 정해지는 방식을 동적 스코프(Dynamic Scope)라고 부른다.

자바스크립트 및 대부분의 언어들은 어휘적 스코프(Lexical Scope) 방식을 사용한다.