스코프가 함수의 중첩에 의해 계층적으로 연결되어 있는 것을 스코프 체인이라고 한다. 자바 스크립트는 스코프 체인을 통해 변수를 참조하는 코드의 스코프에서 상위 방향으로 이동하며 선언된 변수를 검색한다.
var x = 1;
fucntion foo(){
var x = 10;
bar();
}
function bar(){
console.log(x);
}
foo(); // 1
bar(); // 1
var x = 'g'
function foo(){
console.log(x);
var x = 'l';
}
foo();// undefined
위 코드에서 언뜻 foo();의 결과로 'g'를 출력할 것으로 보이지만 그렇지 않다. foo 내부에서 x가 선언됐기 때문에 런타임 이전에 x는 undefined로 초기화된 상태다. 이와 같이 변수 선언은 스코프의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트의 특징을 호이스팅이라고 한다.
(function (){
var foo = 10;
})();
console.log(foo)
var MYAPP = {};
MYAPP.name = 'Lee';
<script type="module"...></script>
var 키워드로 선언한 변수는 함수 스코프
let, const 키워드로 선언한 변수는 블록 레벨 스코프
console.log(a); // referenceError: a is not defined
let a;
console.log(a); // undefined
a = 1;
console.log(a); // 1
let으로 선언한 변수는 var와 달리'선언'과 '초기화'가 분리되어 진행된다. 런타임 이전에 선언 단계가 먼저 실행되고 초기화 단계는 런타임에 실행되기 때문에 초기화가 진행되기 전에 변수에 접근하려고 하면 위와 같이 참조 에러가 발생한다. 이때 스코프의 시작 지점부터 초기화 시작 지점까지 변수를 참조할 수 없는 구간을 TDZ라고 부른다.
이는 얼핏 let이 var 키워드처럼 호이스팅이 발생하지 않는 것처럼 보이지만 사실 let도 호이스팅이 일어난다. 아래 코드를 확인해 보자.
let foo = 1;
{
console.log(foo) // referenceError : Cannot access 'a' before initialization
let foo = 2;
}
let 키워드가 호이스팅이 발생하지 않는다면 console.log(foo)에서 1이 출력되어야 하지만 실제로는 참조 에러가 발생한다. 이는 let 키워드에도 호이스팅이 적용되기 때문에 블록 스코프 내에서 (let foo = 2) 선언만 되었고 초기화가 아직 이루어지지 않은 상태(TDZ)의 foo를 참조하려 했기 때문이다.
const a; // referenceError: Missing initializer in const declaration
const b = 2;
{
console.log(b); // referenceError: Cannot access 'b' before initialization
const b = 1;
}
const는 반드시 선언과 동시에 초기화해야한다. 그러나 이것은 코드 작성 단계에서의 요구사항이고 const 키워드로 선언한 변수도 let과 마찬가지로 선언(런타임 이전)과 초기화(런타임)가 분리되어 실행되며 따라서 호이스팅, TDZ 또한 존재한다.
📖 모던자바스크립트 딥다이브 13장 스코프(189p)
📖 모던자바스크립트 딥다이브 14장 전역 변수의 문제점(201p)
📖 모던자바스크립트 딥다이브 15장 let, const 키워드와 블록 레벨 스코프(208p)