블록 없는 스코프: 자바스크립트 장난
출처: Dev.to
var
var에 대해서는 변한 것이 없습니다. 함수 스코프 또는 전역 스코프만 사용합니다. for 선언부에 var를 쓰면 해당 변수가 포함하고 있는 함수(또는 전역 스코프)에서 접근 가능해지고, 루프가 끝난 뒤에도 값이 유지됩니다.
var a = 1;
for (var a = 2; a console.log(i), 10);
// logs 0‑9
for (let i = 0; i console.log(i), 10);
MDN – For 문 초기화
let으로 선언된 변수는 주변 블록이 아니라 for 문 자체에 스코프가 제한됩니다.
ES2015 이후에 “블록 스코프”라고 말할 때 실제로는 렉시컬 스코프—소스 코드에서 변수가 쓰인 위치에 의해 결정되는 스코프—를 의미합니다. let은 루프 문에만 독점적인 렉시컬 스코프를 만들며, 루프 본문이 블록이 아니어도 마찬가지입니다.
Function‑scope example
임의의 블록을 언제든 만들 수 있으며, 각 블록은 자체 스코프를 가집니다.
function scope(a = 2) {
console.log(a); // 2
// 함수 스코프 내에서 변수 재선언
var a = 'a';
console.log(a); // 'a'
// 함수 스코프 내에서 var 재선언
for (var a = 8; a < 10; a += 1) console.log(a); // 8, 9
console.log(a); // 10 (var는 함수 스코프)
// let은 루프에 새로운 블록 스코프를 생성
for (let a = 1; a < 3; a += 1) console.log(a); // 1, 2
console.log(a); // 10 (함수 스코프의 a로 돌아감)
{
// 자체 스코프를 가진 자유 블록
let a = 'random block';
console.log(a); // 'random block'
}
console.log(a); // 10 (함수 스코프 복귀)
// 선언이 섞인 루프
for (a = 1; a < 5; a += 1) {
var b = 2; // 함수 스코프, 호이스팅됨
let c = 3; // 블록 스코프
console.log(a, b, c);
}
console.log(b); // 2 (여전히 접근 가능)
try {
console.log(c); // ReferenceError
} catch (e) {
// `d`는 catch 블록에 스코프가 한정되고, `e`는 잡힌 오류 바인딩
let d = 4;
console.log(d, e);
}
}
scope();
출력 (예시):
2
a
8
9
10
1
2
10
random block
10
1 2 3
2 2 3
3 2 3
4 2 3
2
4 ReferenceError: c is not defined
Global scope
함수 내부에서 var로 선언한 변수는 전역 스코프에 추가되지 않습니다. 전역에서 접근하려 하면 ReferenceError가 발생합니다.
// ReferenceError – 전역 스코프에 선언되지 않음
console.log(a);
Summary
var는 함수 스코프(또는 전역 스코프)를 가지며, 오류 없이 재선언할 수 있습니다.let(및const)은 렉시컬 스코프를 가집니다.for루프에서는 초기화 표현식이 루프 문 자체의 스코프를 갖게 되며, 루프 본문이 블록이 아니어도 이 스코프는 루프가 진행되는 동안 유지됩니다.- 임의의 블록(
{ … })을 어디에든 삽입해 추가적인 렉시컬 스코프를 만들 수 있습니다. - 함수 매개변수는 함수의 렉시컬 스코프 내에서 변수처럼 동작하는 바인딩입니다.
이러한 미묘한 차이 때문에 루프 본문에 명시적인 중괄호가 없더라도 let은 “블록 스코프”를 가진 것처럼 보입니다.