목표
15장 블록 레벨 스코프(let, const)
15-1. var 키워드로 선언한 변수의 문제점
- ES5까지 변수를 선언할 수 있는 유일한 방법은 var 키워드를 사용하는 것이었다.
- 하지만, var 키워드를 사용하면 다음과 같은 문제들이 발생한다.
15-1-1. 변수 중복 선언 허용
var x = 1;
var y = 1;
var x = 100;
var y;
console.log(x);
console.log(y);
100
1
- var 키워드로 선언한 변수를 중복 선언하면 초기화문의 유무에 따라 다르게 동작한다.
초기화문이란?
- 변수 선언과 동시에 초기값을 할당하는 문장을 말한다.
var number = 1;
- 위 코드에서 초기화문으로 다시 중복 선언한 x 변수는 자바스크립트 엔진에 의해 var 키워드가 없는 것처럼 동작하고 초기화문이 아닌 y 변수는 무시 된다.
- 에러가 발생하지 않기 때문에 동일한 이름의 변수가 이미 선언되어 있는 것을 모르고 같은 이름의 초기화문이 존재한다면 의도치 않게 먼저 선언된 변수의 값이 변경되는 부작용이 발생한다.
15-1-2. 함수 레벨 스코프
- var 키워드로 선언한 변수는 오로지 함수의 코드 블록만을 지역 스코프로 인정한다.
- 따라서, 함수 외부에서 선언한 변수를 코드 블록 내에 다시 선언하면 그 변수 또한 전역 변수가 된다.
var x = 1;
if (true) {
var x = 3;
}
console.log(x);
for (var x = 0; x < 10; x++) {
continue;
}
console.log(x);
3
10
- 함수 레벨 스코프는 전역 변수를 남발할 가능성이 높다. 이로 인해 변수가 중복 선언되는 경우가 발생한다.
15-1-3. 변수 호이스팅
- var 키워드로 변수를 선언하면 변수 호이스팅에 의해 변수 선언문 이전에 참조할 수 있다. (단, 할당문 이전에 참조하면 undefined를 반환한다.)
console.log(number);
number = 1
console.log(number);
var number;
undefined
1
- 위 코드는 변수 호이스팅에 의해 에러가 발생하지는 않지만 프로그램의 흐름상 맞지 않으며 다른 사람이 코드를 읽을 때 가독성을 저해한다.
15-2. let 키워드
- 위의 문제들을 보완하기 위해 ES6에서는 새로운 변수 선언 키워드인 let과 const를 도입했다.
- var와 let 키워드의 차이점은 다음과 같다.
15.2.1 변수 중복 선언 금지
- var 키워드로 이름이 동일한 변수를 중복 선언하면 아무런 에러가 발생하지 않아 중복 선언하면서 값까지 할당했다면 의도치 않게 먼저 선언된 변수 값이 재할당되어 변경되는 부작용이 발생한다.
- 하지만, let 키워드로 이름이 같은 변수를 중복 선언하면 문법 에러(SyntaxError)가 발생한다.
var varValue = "old";
var varValue = "new";
let letValue = "old";
let letValue = "new";
"SyntaxError: Identifier 'letValue' has already been declared"
15.2.2 블록 레벨 스코프
- var 키워드로 선언한 변수는 오직 함수의 코드 블록만을 지역 스코프로 인정하는 함수 레벨 스코프를 따른다.
- 하지만, let 키워드로 선언한 변수는 모든 코드 블록(함수, if문, for문, while문, try/catch 문 등)을 지역 스코프로 인정하는 블록 레벨 스코프를 따른다.
var valValue = "old";
let letValue = "old";
{
var valValue = "new";
let letValue = "new";
}
console.log(`valValue: ${valValue}`);
console.log(`letValue: ${letValue}`);
"valValue: new"
"letValue: old"
- 전역으로 선언된 letValue와 코드 블록 내에서 선언된 letValue는 별개의 변수이므로 전역에서는 코드 블록 안에 있는 letValue 변수를 참조할 수 없다.
15.2.3 변수 호이스팅
- var 키워드로 선언한 변수와 달리 let 키워드로 선언한 변수는 변수 호이스팅이 발생하지 않는 것처럼 동작한다.
console.log(letValue);
let letValue;
"ReferenceError: Cannot access 'letValue' before initialization"
- let 키워드로 선언한 변수를 변수 선언문 이전에 참조하면 참조 에러(ReferenceError)가 발생한다.
console.log(valValue);
var valValue;
undefined
- var 키워드로 선언한 변수는 런타임 이전에 자바스크립트 엔진에 의해 암묵적으로 "선언 단계"와 "초기화 단계"가 한번에 진행된다.
- 즉, 선언 단계에서 스코프(실행 컨텍스트의 렉시컬 환경)에 변수 식별자를 등록해 자바스크립트 엔진에 변수의 존재를 알린다. 그 후 즉시 초기화 단계에서 undefined로 변수를 초기화한다.
- 자세한 내용은 변수를 참조하면 된다.
- let 키워드로 선언한 변수는 "선언 단계"와 "초기화 단계"가 분리되어 진행된다.
- 즉, 런타임 이전에 자바스크립트 엔진에 의해 선언 단계가 먼저 실행되지만 초기화 단계는 변수 선언문에 도달했을 때 실행된다.
- let 키워드로 선언한 변수는 스코프의 시작 지점부터 초기화 단계 시작 지점까지 변수를 참조할 수 없다. 이 구간을 일시적 사각지대(TDZ, Temporal Dead Zone)라고 부른다.
- 결국 let 키워드로 선언한 변수는 참조 에러를 반환하기 때문에 변수 호이스팅이 발생하지 않는 것처럼 보인다. 하지만 그렇지 않다.
let letValue = "old";
{
console.log(letValue);
let letValue = "new";
}
"ReferenceError: Cannot access 'letValue' before initialization"
- let 키워드로 선언한 변수가 호이스팅이 발생하지 않는다면 위 코드에서는 전역 변수의 letValue의 값인 old를 출력해야 한다.
- 하지만, 호이스팅이 발생하기 때문에 지역 변수의 letValue의 식별자가 등록되어 참조 에러가 발생한다.
- 자바스크립트는 ES6에서 도입된 let, const를 포함하여 모든 선언(var, let, const, function, class)을 호이스팅한다.
- 단, ES6에서 도입된 let, const, class의 경우 호이스팅이 발생하지 않는 것처럼 동작한다.
15.2.4 전역 객체와 let
- var 키워드로 선언한 전역 변수와 함수는 전역 객체 브라우저에서 window의 프로퍼티가 된다.
var valValue = "val";
function test() {}
console.log(window.valValue);
console.log(window.test);
"val"
ƒ test() {}
- 하지만, let 키워드로 선언한 전역 변수는 window의 프로퍼티가 아니다.
- let 전역 변수는 보이지 않는 개념적인 블록(전역 레시컬 환경의 선언적 환경 레코드) 내에 존재하게 된다.
let letValue = "let";
console.log(window.letValue);
undefined
15-3. const 키워드
- const 키워드는 상수를 선언하기 위해 사용된다. 특징은 let 키워드와 대부분 동일하지만 다른 특징이 있다.
15-3-1. 선언과 초기화
- const 키워드로 선언한 변수는 반드시 선언과 동시에 초기화를 해줘야 한다.
const TAX_RATE = 0.1;
- 초기화를 해주지 않으면 문법 에러(Syntax Error)가 발생한다.
const TAX_RATE;
"SyntaxError: Missing initializer in const declaration"
15-3-2. 재할당 금지
- let 키워드로 선언한 변수는 재할당이 가능하다.
let price = 1000;
price = 2000;
- 하지만, const 키워드로 선언한 변수는 재할당이 불가능하다.
const TAX_RATE = 0.1;
TAX_RATE = 0.2;
"TypeError: Assignment to constant variable."
15-3-3. 상수
- 상수는 상태 유지와 가독성, 유지보수의 편의를 위해 사용한다.
let price = 3000;
price = price + (price * 0.1);
- 위 코드 내에 사용한 0.1은 어떤 의미로 사용했는지 명확히 알기 어렵기 때문에 가독성이 좋지 않다.
- 또한, 세율을 의미하는 0.1은 쉽게 바뀌지 않는 값이며 고정적이다.
- 따라서, 세율을 상수로 정의하면 값의 의미를 쉽게 파악할 수 있다.
const TAX_RATE = 0.1;
let price = 3000;
price = price + (price * TAX_RATE);
- 일반적으로 상수의 이름은 대문자로 선언하며 여러 단어로 이루어진 경우에는 언더스코어(_)로 구분해서 스네이크 케이스로 표현하는 것이 일반적이다.
15-3-4. const 키워드와 객체
- const 키워드로 선언된 변수에 원시 값을 할당하면 값을 변경할 수 없다.
- 하지만, const 키워드로 선언된 변수에 객체를 할당한 경우에는 값을 변경할 수 있다.
const product = {
price: 1000
}
product.price = 3000;
console.log(product);
{ price: 3000 }
15-4. [정리] var, let, const
- ES6를 사용한다면 var 키워드는 사용하지 않는것이 좋다.
- 변수 선언에는 기본적으로 const를 사용하고 재할당이 필요한 경우에 한정해 let 키워드를 사용한다.
- 변경이 발생하지 않고 재할당이 필요 없다면 const 키워드를 사용한다.