4. scope


Scope: 변수의 유효범위

자바스크립트에는 세 가지의 유효범위, 즉 ‘스코프(scope)’가 있습니다. let 또는 const로 선언한 변수는 세 스코프 모두의 영향을 받습니다.

1. 전역 스코프: global scope

전역 스코프는 자바스크립트 코드의 가장 큰 유효범위입니다. ‘전역(全域)’이란 ‘모든 지역’을 뜻합니다. 코드의 모든 지역에서 접근할 수 있는 영역임을 표현한 한자어입니다. 전역 스코프에서 선언한 변수를 ‘전역 변수(global variable)’라고 합니다. 전역 변수는 전역 스코프 내에서만 존재하며, 외부에서는 접근할 수 없습니다. 반대로 전역 변수는 코드 내부 어디서든 접근할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
let a = 1
const b = 2

const functionC = () => {
console.log(a, b) // 1 2
const functionD = () => {
console.log(a, b) // 1 2
}
functionD()
}
functionC()

전역 스코프 및 전역 변수와 대칭점에 있는 표현으로 ‘지역 스코프(local scope)’ 및 ‘지역 변수(local variable)’가 있습니다. 안티패턴인 eval 스코프(eval 명령에 의해 생성되는 스코프)를 제외하면, 자바스크립트의 모든 스코프는 전역 스코프이거나 지역 스코프입니다.

2. 함수 스코프: function scope

함수 내에서 선언한 변수는 함수 내에서만 유효하며 함수 외부에서는 접근할 수 없습니다. 반면 해당 함수 내부에서는 TDZ(1-6-2에서 소개합니다)에 속하지 않는 한 어디서나 참조할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
const functionA = () => {
let a = 1
const b = 2
const functionB = () => {
let c = 3
console.log(a, b, c) // 1 2 3
}
functionB()
console.log(a, b, c)
// chrome & firefox | Error: c is not defined.
// safari | Error: Can't find variable: c
}
functionA()
1
2
3
4
5
6
7
8
9
10
11
12
13
const functionA = () => {
let a = 1
const b = 2
const functionB = () => {
let c = 3
console.log(a, b, c) // 1 2 3
}
functionB()
}
functionA()
console.log(a, b, c)
// chrome & firefox | Error: a is not defined.
// safari | Error: Can't find variable: a

위 두 코드는 console.log(a, b, c) 의 위치만 다르고 나머지는 완전히 동일합니다. 우선 functionB 내부에서의 console.log 명령에 의해 1 2 3이 잘 출력되었습니다. functionB 내부에서는 functionA 내부에서 선언한 변수 a 및 b와 functionB 내부에서 선언한 변수 c 모두를 참조할 수 있음을 확인했습니다.첫 번째 코드에서는 functionA 내부에서는 어떨지를 확인해 보았는데 에러가 발생했습니다. 변수 c가 ‘정의되지 않았다’거나, ‘찾을 수 없다’고 합니다. functionA에서는 functionB에서 선언한 변수 c의 존재조차 알지 못하는 것입니다. 두 번째 코드에서는 전역 스코프에서의 결과를 확인해 보았는데 역시 에러가 발생했습니다. 당장 a부터 찾을 수 없기 때문에 뒤의 b, c를 찾으려는 시도를 하기도 전에 이미 중단되고 말았습니다. 전역 스코프에서는 functionA에서 선언한 변수들의 존재조차 알지 못하는 것입니다.

함수 스코프는 실행 컨텍스트(execution context)와 밀접한 연관이 있습니다. 실행 컨텍스트와 스코프 및 스코프 체이닝, 클로저 등에 대한 자세한 내용은 제 저서인 코어 자바스크립트를 참고하시기 바랍니다.

3. 블록 스코프: block scope

함수를 제외한 모든 문(statement) 형태의 문법(if문, for문, while문, switch/case문, try/catch문 등) 내부에서 선언한 변수는 문(중괄호) 내에서만 유효하며, 블록 외부에서는 접근할 수 없습니다.

‘식’과 ‘문’에 대하여

어떤 하나 이상의 명령을 수행하는 단위를 통틀어 ‘문(statement)’라 합니다. 이 중에서 명령 수행의 결과로 어떤 값을 도출하는 경우를 특별히 ‘식 또는 표현식(expression)’이라 합니다. 즉 식은 문의 부분집합입니다. 그러나 일반적으로는 식과 문을 보다 명확히 구분하기 위해서, 값을 도출하지 않는 경우만을 일컬어 ‘문’이라고 하는 경우가 많습니다. 이 방식에 의하면 식과 문은 서로 부분집합이 아닌 여집합 관계가 됩니다. 저는 이 방식을 따르겠습니다. 앞으로는 ‘식’은 수행 결과 값이 되는 경우를, ‘문’은 수행 결과 값을 도출하지 않고 넘어가는 경우를 말하는 것으로 이해해 주세요.

1
2
3
4
5
6
7
8
9
10
if (true) {
// 조건문 (1)
const a = 1
console.log(a) // 결과: 1
if (a > 0) {
// 조건문 (2)
console.log(a) // 결과: 1
}
}
console.log(a) // 결과: Error: a is not defined.

조건문 (1) 내에서 선언한 변수 a는 조건문 (1)의 블록 스코프 내부 어디서든 접근할 수 있습니다. 조건문 (2) 역시 조건문 (1)의 블록스코프에 접근할 수 있습니다. 그러나 조건문 (1)의 외부에 해당하는 8행에서는 접근할 수 없으므로 에러가 발생했습니다.

블록 스코프는 for문, if문 등 ‘문 형태의 문법’에 의해서만 발생하는 것이 아닙니다. 단순히 중괄호로 묶기만 한 경우에도 성립합니다.

1
2
3
4
5
6
{
// (문 시작)
const a = 1
console.log(a) // 결과: 1
} // (문 종료)
console.log(a) // 결과: Error: a is not defined.

실제로 이런 식의 코드를 작성할 이유는 전혀 없습니다. 이렇게 작성하더라도 블록 스코프가 발생한다는 사실을 소개하고자 하였을 뿐입니다. 이런 의미 없는 문은 절대 작성하지 맙시다.