프론트엔드/JavaScript

[JavaScript] var, let, const 차이

기존의 문법인 var와 ES6에서 새로 추가된 let, const을 비교하고자 한다.

추가로 호이스팅과 그로 인한 TDZ 현상에 대해서도 알아보겠다.


var

ES6 이전의 변수 선언 방식이다.

유연한 방식으로 인해 발생하는 문제가 많아 최대한 사용하지 않는 것이 좋다.

 

1. 함수 레벨 스코프

if문 안의 foo와 if문 밖의 foo는 같은 스코프로 취급하여 동일한 변수가 된다.

var foo='hello';

if(true) {
  var foo = 'hello if';
}

console.log(foo); // hello if

 

2. 중복 선언 가능

같은 변수를 여러 번 선언해도 오류가 나지 않는다.

var foo='hello1';
var foo='hello2';

console.log(foo); // hello2

 

3. var의 호이스팅

var는 호이스팅으로 인해 선언보다 호출이 먼저 되어도 오류 없이 정상 실행된다.

console.log(foo); // undefined

var foo;

 


let

ES6부터 도입된 변수 선언 방식이다.

 

1. 블록 레벨 스코프

let과 const는 var와 달리 블록 레벨 스코프를 가진다.

즉 let과 const로 선언된 변수는 해당 코드 블록 내에서만 유효하며 외부에서는 참조할 수 없다.

let foo = 123;

{
   let foo = 456;
   let bar = 456;
}

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

 

2. 중복 선언 불가

let은 var와 달리 동일한 이름을 갖는 변수를 중복해서 선언할 수 없다.

var foo = 123;
var foo = 456; //중복 선언 허용

let foo = 123;
let foo = 456; // Uncaught SyntaxError: Identifier 'bar' has already been declared

 

3. let의 호이스팅 (TDZ)

var와 달리 let 변수를 선언 이전에 참조한다면 ReferenceError가 발생한다.

이는 Temporal Dead Zone(TDZ) 현상 때문이다.

console.log(foo); // undefined
var foo;

console.log(bar) //Error:Uncaught ReferenceError:bar is not defined
let bar;

 

Temporal Dead Zone(TDZ)

Temporal Dead Zone(TDZ) 현상은 일시적 사각지대라고도 불린다. 

 

var선언과 초기화동시에 진행된다.

반면 let은 선언과 초기화분리되어 진행된다.

선언 단계에서 스코프에 변수를 등록은 하지만 초기화변수 선언문에 도달했을 때 이루어진다.

 

그래서 초기화 이전에 변수에 접근하려고 하면 참조 에러(ReferenceError)가 발생한다.

변수가 아직 초기화되지 않았기 때문이다.

 

이렇게 스코프의 등록 지점부터 초기화 지점 전까지의 변수를 참조할 수 없는 구간TDZ라 부른다.

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

let foo; // 초기화 단계
console.log(foo); // undefined

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

간단한 예시이다.

 

let foo = 1; // 전역 변수

{
  console.log(foo); // ReferenceError: foo is not defined
  let foo = 2; // 지역 변수
}

또한 지역 변수에도 TDZ 현상이 발생할 수 있다.

예시에서 전역 변수 foo의 값이 출력되지 않고 지역 변수 foo에 참조 에러(ReferenceError)가 발생한다.

 


const

const는 ES6 부터 도입된 상수 선언 방식이다.

 

1. 블록 레벨 스코프

const는 let과 마찬가지로 블록 레벨 스코프를 갖는다.

{
  const FOO = 10;
  console.log(FOO); //10
}
console.log(FOO); // ReferenceError: FOO is not defined

 

2. 재할당 불가

let은 재할당이 자유롭지만 const재할당불가능하다.

const FOO = 123;
FOO = 456; // TypeError: Assignment to constant variable.

 

또한 const는 반드시 선언과 동시에 할당이 이루어져야 한다.

const FOO; // SyntaxError: Missing initializer in const declaration

 

3. 의미론적 사용

상수는 가독성과 유지보수를 위한 장점도 크기 때문에 자주 사용하면 좋다.

자세한 설명은 아래 글을 참고하면 좋다.

 

[JavaScript] Clean Code - 1. 변수명 짓는 방법

 

[JavaScript] Clean Code - 1. 변수명 짓는 방법

1. 의미 있고 발음하기 쉬운 변수명을 사용한다. const yyyymmdstr = moment().format('YYYY/MM/DD'); // BAD const currentDate = moment().format('YYYY/MM/DD'); // GOOD! 2. 같은 의미의 변수를 여러 개 만들..

seokzin.tistory.com

 

4. const와 객체

(이 부분이 const를 처음 접할 때 가장 헷갈렸던 부분이다.)

 

알다시피 const는 재할당이 불가능하다.

const 변수의 타입이 객체인 경우에도 객체에 대한 참조를 변경할 수 없다.

 

하지만 객체의 프로퍼티는 보호되지 않는다.

재할당불가능하지만 할당된 객체의 내용 변경(프로퍼티의 추가, 삭제, 수정)은 가능하다.

const user = { name: 'Lee' };

// const 변수는 재할당이 금지
// user = {}; // TypeError: Assignment to constant variable.

// 객체의 내용은 변경 가능
user.name = 'Kim';

console.log(user); // { name: 'Kim' }

 


결론

  • var는 웬만하면 사용하지 않는다.
  • 되도록 let보다 const를 사용한다.
    • let 변수가 있다면 언제 바뀔지 모른다는 생각을 가지고 코드를 읽어야 한다.
    • 반면 const는 초기화, 선언, 할당까지 되어 있으니 변경되지 않을 것이라는 생각을 가지고 코드를 읽을 수 있다.
  • 객체 타입 변수 선언에는 되도록 const를 사용한다.
    • const로 선언해도 객체에 대한 변경은 자유롭게 가능하다.
    • 또한 의도치 않은 재할당을 방지할 수도 있다.