모든 식별자(변수 이름, 함수 이름, 클래스 이름 등)는 자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효 범위가 결정된다. 이를 스코프(scope)라 한다. 즉, 스코프는 식별자가 유효한 범위를 말한다.
코드는 전역(global)과 지역(local)로 구분할 수 있다. 이 때 변수는 자신이 선언된 위치(전역 또는 지역)에 의해 자신이 유효한 범위인 스코프가 결정된다.
즉, 전역에 선언된 변수는 전역 변수이고, 지역에서 선언된 변수는 지역 스코프를 갖는 지역 변수다.
| 구분 | 설명 | 스코프 | 변수 |
|---|---|---|---|
| 전역 | 코드의 가장 바깥 영역 | 전역 스코프 : 코드 어디에서나 참조할 수 있다 | 전역 변수 |
| 지역 | 함수 몸체 내부 | 지역 스코프 : 자신이 선언된 지역과 하위 지역에서만 참조할 수 있다 | 지역 변수 |
C나 자바를 비롯해 대부분의 프로그래밍 언어는 함수 몸체만이 아니라 모든 코드 블록(if, for, while, try/catch 등)이 지역 스코프를 만든다. 이런 특성을 블록 레벨 스코프라고 한다.
하지만 자바스크립트에서는 var 키워드로 선언된 변수는 오로지 함수의 코드블록(함수 몸체)만을 지역 스코프로 인정한다. 이런 특성을 함수 레벨 스코프라고 한다.
function first(){
const age = 30;
if(age >= 30){
var millenial = true;
}
console.log(millenial); // true:var 변수는 함수 레벨 스코프를 가진다
}
자바스크립트에 블록 레벨 스코프는 es6 문법부터 let, const 변수와 함께 도입되었다.
es6 이후 도입된 let, const 변수는 블록 레벨 스코프를 가진다.
function first(){
const age = 30;
if(age >= 30){
const decade = 3;
let name = 'Sunny';
}
console.log(decade); // ReferenceError: decade is not defined
console.log(name); // ReferenceError: name is not defined
}
또, es6 이후로는 아래처럼 strict mode 사용시 함수 선언문도 블록 레벨 스코프를 가진다.
function first(){
const age = 30;
if(age >= 30){
function second(){
console.log(`I am ${age} years old`);
}
}
second(); // ReferenceError: second is not defined
}
변수를 참조할 때, 자바스크립트 엔진은 스코프 체인을 통해 변수를 참조하는 코드의 스코프에서 시작하여 상위 스코프 방향으로 이동하며 선언된 변수를 검색한다.
이를 통해 상위 스코프에서 선언한 변수를 하위 스코프에서도 참조할 수 있다.
'use strict'
function calcAge(birthYear){
const age = 2024 - birthYear;
console.log(firstName); // 'Eunsun': 상위스코프인 전역 스코프 변수 참조
return age;
}
const firstName = 'Eunsun'
calcAge(1994);
자바스크립트에서는 함수 정의가 평가되는 시점에 상위 스코프가 정적으로 결정된다. 이를 렉시컬 스코프, 혹은 동적 스코프라고 한다.
함수가 호출된 위치는 상위 스코프 결정에 어떤 영향도 주지 않는다.
function first(){
const a = 1;
const b = 2;
second();
}
function second(){
console.log("a + b: ",a + b);
}
first();
위 코드에서 second 함수는 first 함수 내부에서 호출 되지만, second 함수의 상위 스코프는 second 함수가 정의된 시점에서 결정된 전역 스코프이기 때문에, first 함수에 선언된 변수 a, b에 접근할 수 없다.