자바스크립트 호이스팅과 스코프
- 자바스크립트 엔진에서는 코드를 실행하기 전에 변수와 함수 선언을 먼저 처리한다.
- 이로 인해 마치 선언이 코드의 최상단으로 끌어올려진 것처럼 동작하는 것을 호이스팅이라고 부른다.
- 또한 변수나 함수가 어떤 범위(scope)에서 유효한지에 따라 여러 가지 특징적인 동작을 보인다.
호이스팅
- 자바스크립트 엔진은 코드를 실행하기 이전에, 변수 선언문이나 함수 선언문을 미리 해석하여 선언을 위한 메모리를 할당한 뒤, 코드가 실행될 때 변수나 함수를 미리 사용할 수 있게 준비해 둔다.
- 예시:
console.log(score); // undefined
var score;
score = 100;
console.log(score); // 100
- 일반적으로 "아직 선언되지 않은 변수를 console.log로 출력하면 에러가 발생해야 하는 것 아닌가?"라고 생각할 수도 있지만, 자바스크립트에서는 위 예시처럼 `undefined`가 출력된다.
- 이는 `var score;` 선언이 런타임 이전에 미리 처리되었기 때문이다. 이처럼 선언이 코드의 최상단으로 끌어올려진 것처럼 동작하는 것을 호이스팅이라고 부른다.
변수 호이스팅
- 위 예시에서 `var` 키워드를 사용한 변수는 선언과 동시에 초기화가 되므로 `undefined`가 출력되었다.
- `let`, `const`로 선언한 경우에는 어떤 결과가 될까?
console.log(age); // ReferenceError
let age = 30;
- 이 경우, age 변수가 호이스팅되었더라도, 초기화는 실제 코드가 도달했을 때 이루어지므로 그 이전 시점에 참조하려고 하면 에러가 발생한다.
함수 호이스팅
- 함수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 현상을 함수 호이스팅이라고 일컫는다. 하지만 변수 호이스팅과는 미묘한 차이가 존재한다.
// 함수 참조
console.dir(add); // f add(x, y)
console.dir(sub); // undefined
// 함수 호출
console.log(add(2, 5)); // 7
console.log(sub(2, 5)); // TypeError: sub is not a function
// 함수 선언문
function add(x, y) {
return x + y;
}
// 함수 표현식
var sub = function (x, y) {
return x - y;
};
- 함수 선언문을 통해 정의된 함수는 런타임 이전(평가 단계)에 함수 객체가 생성되고, 식별자에 할당되어 있다.
- 따라서 코드 최상단에 선언된 것처럼 동작하여 함수 호출을 해도 문제가 없다.
- 함수 표현식은 변수에 함수를 할당하는 문이므로, 변수 호이스팅만 일어나고 함수 객체 자체는 런타임 시점에 할당된다.
- 따라서 초기화 이전에 함수를 호출하면 에러(TypeError)가 발생하게 된다.
스코프(Scope)
- 변수나 함수가 유효하게 작동하는 범위
- "코드 어디에서 해당 변수/함수에 접근할 수 있는가?"를 정하는 영역
- 스코프는 크게 전역 스코프와 지역 스코프로 구분된다.
- 전역 스코프: 코드 어디서든 접근 가능한 범위
- 지역 스코프: 특정 코드 블록(함수, 블록 등) 내부에서만 접근 가능한 범위
스코프 체인(Scope Chain)
- 변수를 참조할 때 자바스크립트 엔진은 스코프 체인을 통해 현재 스코프에서 시작하여 상위(외부) 스코프 방향으로 이동하며 변수를 검색한다.
- 예시:
const x = "global x";
const y = "global y";
function outer() {
const y = "outer's local y";
function inner() {
const z = "inner's local z";
console.log(x); // "global x"
console.log(y); // "outer's local y"
console.log(z); // "inner's local z"
}
inner();
// console.log(z); // ReferenceError: z is not defined
}
outer();
- inner 함수 내부에서는 outer 함수에 정의된 y에 접근이 가능하지만, outer 함수 내부에서는 inner 함수의 z에 접근할 수 없다. 이것이 스코프 체인과 렉시컬 스코프(정적 스코프)의 특징이다.
- 해당 코드에 대한 스코프 체인은 아래 그림과 같이 구성되어 있다.
스코프 레벨
1. 함수 레벨 스코프
- var 키워드로 선언된 변수나, 함수 선언문으로 만들어진 함수는 함수 레벨 스코프를 갖는다고 볼 수 있다.
- 이는 "함수 내부 전체에서만 유효"하다는 의미이다.
function foo() {
if (true) {
var color = 'blue';
}
console.log(color); // blue
}
foo();
2. 블록 레벨 스코프
- `let`이나 `const`로 선언한 변수는 블록 레벨 스코프를 따르므로, 해당 블록(`{ ... }`) 내부에서만 유효하며 블록을 벗어나면 참조할 수 없다.
function foo() {
if (true) {
let color = 'blue';
console.log(color); // blue
}
console.log(color); // ReferenceError
}
foo();
3. 렉시컬 스코프
- “함수의 선언 위치에 의해 상위 스코프가 정해진다”는 언어적 특성 또는 개념이다.
- 정적 스코프(Static Scope) 라고도 불린다.
렉시컬 스코프의 동작
- 함수가 선언될 당시의 스코프 체인이 함수의 상위 스코프로 결정된다.
- 함수가 호출된 위치는 스코프 결정에 영향을 미치지 않는다.
- 예시:
var a = 10;
function first() {
var a = 20;
second();
}
function second() {
console.log(a);
}
first(); // ?
second(); // ?
- 실행 결과로는 10, 10이 출력된다. second함수가 first함수 안에서 호출되었다고 하더라도 second함수가 선언된 위치는 전역 스코프이므로 전역 변수인 a를 참조하여 10이 출력되는 것이다.
- 이러한 개념은 클로저와 매우 밀접하게 연관되어 있다. 클로저는 다음 게시물에서 확인해본다.
참고
'WEB > JavaScript' 카테고리의 다른 글
[JavaScript] 클로저, 렉시컬 환경, 실행 컨텍스트 (0) | 2025.01.05 |
---|---|
[JavaScript] 변수 선언, 함수 정의 및 형태 (0) | 2025.01.05 |
[JavaScript] 객체 메소드(Object methods) (1) | 2024.01.09 |
[JavaScript] 기능 모듈화 시키기 (0) | 2023.10.16 |
[JavaScript] 화살표 함수, Arrow Function (0) | 2023.10.13 |