본문 바로가기
Web/Frontend

[JS] 모던 자바스크립트 딥다이브 15장- var,let,const 키워드

by 콧등치기국수 2023. 8. 19.

ES5까지는 var 키워드만 사용하여 변수를 선언했지만, ES6에서는 var키워드의 단점을 보완하기 위해 let과 const 키워드를 사용해 변수를 선언할 수 있게 되었다. 먼저 var와 let 키워드의 차이점을 중점으로 살펴보자.

 

var & let

1. 변수 중복 선언 허용

1) var

var 키워드로 선언한 변수는 중복 선언이 가능하다.

var x = 1;
var y = 1;

var x = 100;
var y;

console.log(x);  //100
console.log(y);  //1

초기화문이 있는 변수 선언문은 자바스크립트 엔진에 의해 var 키워드가 없는 것처럼 동작하고(var x = 100;), 초기화문이 없는 변수 선언문(var y;)은 무시된다.

이처럼 변수의 중복 선언이 허용되는 경우, 의도치 않게 먼저 선언된 변수와 동일한 변수명으로 선언할 경우 먼저 선언된 변수의 값을 변경해버릴 수 있다. 

 

2) let

변수를 중복 선언할 경우 문법 에러가 발생한다.

let foo = 123;
let foo = 456;  //SystacError: Identifier 'foo' has already been declared.

 

2. 스코프

1) var

함수 레벨 스코프로 오로지 함수의 코드 블록만을 지역 스코프로 인정한다. 따라서 함수 외부에서 var 키워드로 선언한 변수는 모두 전역변수가 된다.

var x = 1;

if(1 == 1){
  //전역변수 x를 중복 선언함
  var x= 10;
}

console.log(x);  //10

for문의 변수 선언문에서 var 키워드로 선언한 변수도 전역 변수가 된다.

var i = 10;

//for문에서 선언한 i는 전역변수이므로 중복 선언된 상황
for(var i=0; i<5; i++) {
  console.log(i);  //0 1 2 3 4
}

console.log(i);    //5

의도치 않게 전역 변수가 중복 선언되거나, 전역 변수의 값이 변경될 수 있다.

 

2) let

모든 코드 블록(함수, if문, for문, while문 등)을 지역 스코프로 인정하는 블록 레벨 스코프를 따른다.

let foo = 1;  //전역 변수

if(true){
  let foo = 2;
  let bar = 3;
}

console.log(foo);  //1
console.log(bar);  //ReferenceError: bar is not defined

let 키워드로 선언된 변수는 블록 레벨 스코프를 따르기 때문에, if문 안에 선언된 foo와 bar 모두 지역 변수다.

따라서 전역에서 생성된 foo와 if문 안에서 선언된 foo는 다른 별개의 변수다. 따라서 전역에서는 bar 변수를 참조할 수 없다. 

스코프

 

3. 변수 호이스팅

var 키워드와 let 키워드 모두 호이스팅이 발생한다.

 

1) var

var 키워드로 선언한 변수는 변수 선언문 이전에 참조할 수 있다. 

//1.변수 호이스팅에 의해 foo변수 선언
//2.foo는 undefined로 초기화
console.log(foo);  //undefined

foo = 123;  //3.값 할당

console.log(foo);  //123

var 키워드로 선언한 변수는 런타임 이전에 선언 단계와 초기화 단계가 한 번에 실행된다. 따라서 변수 선언문 이전에 변수에 접근해도 스코프에 변수가 존재하기 때문에 에러가 발생하지 않는다.

 

2) let

let 키워드로 선언한 변수를 변수 선언문 이전에 참조하면 참조 에러가 발생한다. 

console.log(foo); //ReferenceError: Cannot access 'foo' before initialization
let foo;

let 키워드로 선언한 변수는 선언 단계와 초기화 단계가 분리되어 진행된다. 자바스크립트 엔진에 의해 런타임 전에 호이스팅이 발생해 변수가 선언되지만, var 키워드와 달리 초기화는 함께 진행되지 않는다. 런타임에서 변수 선언문에서 초기화가 되기 때문에, 스코프의 시작 지점부터 초기화가 시작되는 지점까지 구간에서는 변수를 참조할 수 없다. 이러한 구간을 TDZ(Temporal Dead Zone)라고 한다.  

//런타임 이전에 선언됨
//초기화 이전의 TDZ에서는 변수를 참조할 수 없다.
console.log(foo);  //ReferenceError: Cannot access 'foo' before initialization

let foo;  //변수 선언문에서 초기화 단계 실행됨(var 키워드와 차이)
console.log(foo);  //undefined

foo = 1;  //할당
console.log(foo);  //1

let 키워드로 선언한 변수의 생명 주기

이렇게 보니 let키워드로 선언한 변수는 변수 호이스팅이 발생하지 않는 것처럼 보이는데 정말 호이스팅이 발생한 것이 맞는지 살펴보자.

let foo = 1;  //전역 변수
{
  console.log(foo); //ReferenceError: Cannot access 'foo' before initialization
  let foo = 2;      //지역 변수
}

만약 호이스팅이 되지 않았다면, 콘솔에는 전역 변수의 값인 '1'이 출력되었을 것이다. 그러나 런타임 전 호이스팅 단계가 진행되어 전역변수 foo, 지역변수 foo 각각이 별개로 선언되었기 때문에, console.log(foo)는 TDZ 구간이 된다. 따라서ReferenceError가 발생하게 된다.

 

 

const

const 키워드는 상수를 선언하기 위해 사용된다.

 

1. 선언과 초기화

const 키워드로 선언한 변수는 반드시 선언과 동시에 초기화해야한다.

const foo = 1; 
const foo;     // SyntaxError: Missing initializer in const declaration

let 키워드와 같이 블록 레벨 스코프를 가지며 호이스팅이 발생하지 않는 것 처럼 동작한다.

{
  console.log(foo); // ReferenceError: Cannot access 'foo' before initialization
  const foo = 1;
  console.log(foo); // 1
}

// 블록 레벨 스코프를 가진다.
console.log(foo); // ReferenceError: foo is not defined

 

2. 재할당 금지

const 키워드로 선언한 변수는 재할당을 할 수 없다.

const foo = 1;
foo = 10; // TypeError: Assignment to constant variable.

그렇기 때문에 프로그램 전체에서 공통적으로 사용하고 변경되지 않는 값을 const로 선언하면, 유지보수성을 향상시킬 수 있다.  

//세율
const TAX_RATE = 0.1;

//세전 가격
let preTaxPrice = 100;
//세후 가격
let afterTaxPrice = preTaxPrice + (preTaxPrice * TAX_RATE)

console.log(afterTaxPrice);  //110

세율(TAX_RATE) 는 고정된 값이므로 const 로 선언하여 프로그램 내에서 공통적으로 사용한다. 추후 세율이 변경된다고 해도 한 번만 변경해주면 되기 때문에 유지보수에 좋다.

 

 

참고

1. 모던 자바스크립트 딥다이브(이웅모, 위키북스)

2. https://velog.io/@ssomcandy777/let-const-키워드와-블록-레벨-스코프