const fourthStudyLog = "let, const, var";

인터넷에 Javasript를 검색하면 대부분 var키워드를 쓰는 코드들을 많이 볼 수 있다. 하지만 ES6부터는 var이 아닌 let, const로 구분해서 변수 키워드를 사용하도록 배운다. 이 부분은 새로 배우는 초보자들에게 혼란을 준다. 예를 들면, 전에는 var로 작성되어있는 코드를 옮겨적을 때 상당한 고민이 된다. 이에 대한 해답을 찾기 위해서는 세 키워드의 특성을 잘 이해하는 것이 좋다.

대부분의 내용은 <모던 자바스크립트 Deep Dive>를 참고하여서 정리, 작성하였다.

1. var

1-1. 변수 중복 선언

이전에는 변수를 선언할 수 있는 유일한 키워드였다.

var x = 1;
var y = 2;

var x = 100; //같은 스코프 내에서 중복 선언을 허용한다.
var y; //무시된다.

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

var을 사용하여 선언한 변수는 초기화문의 유무에 따라 다르게 작동된다. x의 경우에는 두번째 선언할 때에 초기값이 할당되었기에 x앞의 var 키워드가 없는 것처럼 작동되고, y의 경우 두번째에서 초기값이 할당되지 않았기에 선언문이 무시된것처럼 작동된다.

그렇기에 이전에 선언한 변수의 이름을 그대로 사용할 경우, 이전에 할당한 값이 변경되거나 하는 문제가 발생한다

1-2. 스코프

또한, var 키워드로 선언한 변수는 오로지 함수의 코드 블록만을 지역 스코프로 인정한다.

var x = 1;

if (true) {
  // 위의 x와 아래 x가 모두 전역 스코프이다. x가 중복 선언 되었으므로 x의 값이 변경된다.
  var x = 10;
}

console.log(x); // 10

if문 안에서 선언한 x와 외부에서 선언한 x가 같은 전역 스코프이기에 중복선언이 된다. 따라서 원하지 않게 같은 변수 이름을 쓴다면 외부에서 선언된 변수의 값이 변경된다.

var i = 10;

for (var i = 0; i < 2; i++) {
  console.log(i); // 0,1,2,3,4
}

console.log(i); // 5

for문도 마찬가지로, for문 외부에서 선언한 i와 내부에서 선언된 i가 같은 스코프를 가져서 의도치않게 i의 값이 변경된다.

이처럼 함수 외의 경우에는 의도와 다르게 변수가 중복선언한 것처럼 여겨지는 문제가 있다.

1-3. 변수 호이스팅

변수 호이스팅이란 변수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 것을 의미한다. var 키워드로 변수를 선언하면 변수 호이스팅에 의해서 변수 선언문이 가장 앞으로 끌어올려진 것처럼 동작한다.

console.log(x); // ReferenceError가 발생할 것 같지만, undefined가 나온다.

x = 1;

console.log(x); // 1

var x; //변수 호이스팅으로 변수 선언문이 가장 위로 끌어올려진 것처럼 동작된다.

이는 프로그램 흐름상 적절하지 않고, 가독성을 떨어뜨리기에 오류를 발생시킬 가능성이 높다.

위의 경우를 var 키워드에서 let과 const를 사용하는 경우에는 참조오류(**ReferenceError)**가 발생한다.

위에서 살펴본 var의 단점(중복선언, 스코프문제, 변수 호이스팅 문제)로 ES6에서는 let과 const로 새로운 뱐수 선언 키워드를 도입했다.


2. let

2-1. 변수 중복 선언 금지

var의 경우에는 변수를 중복 선언하는 것이 허용되었지만, let에서는 syntaxerror가 발생한다.

let x;
let x; // "SyntaxError: Identifier 'x' has already been declared

이는 const에서도 같은데, let과 const는 같은스코프 내에서는 변수 중복 선언을 허용하지 않는다.

2-2. 블록 레벨 스코프

let에서는 var과 다르게 모든 코드 블록을 지역 스코프로 인정한다.

let i = 1;

{
  let i = 3;
  let y = 100;

  console.log(i); // 3
  console.log(y); // 100
}

console.log(i); // 1
console.log(y); // ReferenceError: y is not definde

이처럼 let은 블록 단위로 지역 스코프를 가지며, 블록 외에서 선언된 외부 변수와 구분된다. 그렇기 때문에 블록 단위로 함수를 구분하여서 생각하여야 한다.

let i = 100;

function anyting() {
  let i = 22;
  for (let i = 0; i < 4; i++) {
    console.log(i); // 0,1,2,3
  }
  console.log(i); //22
}

console.log(i); //100

같은 i라는 이름의 변수를 사용하였지만 블록 별로 나눠져 다른 변수처럼 여겨진다.

2-3. 변수 호이스팅

let 키워드의 경우에는 변수 선언 전 참조를 할 경우, 참조 오류가 발생한다.

이는 var의 경우에는 런타임 이전에 선언단계초기화 단계가 한번에 진행되기 때문인데, let의 경우에는 선언단계초기화 단계가 분리되어 진행된다.

Javascript 엔진은 런타임 이전에 코드를 미리 읽기 때문에 변수 호이스팅같은 동작이 가능하다. 이 과정이 있기에 var 키워드 선언을 코드의 마지막 줄에 했어도 이미 코드를 미리 읽은 엔진이 변수 선언과 초기화 단계를 거쳐 undefined 값을 낸다.

그러나 let 키워드는 변수 선언문에 도달하기 전에 선언 단계는 실행되긴 하지만 초기화 단계는 변수 선언문에 도달 했을 때 실행된다. 때문에 변수 선언문 전에 변수를 참조하게 되면 referenceError가 발생한다.

//런타임 이전에 엔진은 이미 변수 선언단계를 실행했다.
//그러나 let 키워드로 선언되었기에 아직 초기화 되지 않았다.
console.log(x); //참조오류 발생

let x;
console.log(x); // undefined

x = 1;
console.log(x);

스코프의 시작 지점인 첫번째 console.log(x)부터 변수 x 선언까지는 참조오류가 발생한다. 이 구간을 일시적 사각지대(Temporal Dead Zone:TDZ)라고 부른다.

그렇다고 let 키워드에서 변수 호이스팅이 발생하지 않는 건 아니다. let 키워드에서도 변수 호이스팅은 발생한다.


3. const

const 키워드는 let 키워드와 비슷하다. let과 같이 블록 레벨 스코프를 가지며, 변수 호이스팅이 발생하지 않는 것처럼 동작한다. 다른 특징들이 존재한다.

3-1. 선언과 초기화

const 키워드는 반드시 선언과 동시에 값을 할당해야한다.

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

const x = 1 //반드시 선언과 동시에 초기화(값을 할당)해야한다.

3-2. 재할당 금지

const는 let, var와 달리 재할당이 금지 되어 있다. 즉, 값을 한번 할당하면 변경할 수 없다.

const x = 1;
const x = 2; //SyntaxError: Identifier 'x' has already been declared

재할당 없이는 값을 변경할 수 없는 특성을 이용해서 const키워드는 상수를 표현하는 데 이용한다.

이는 const 키워드로 선언된 변수에 원시 값을 할당한 경우 원시값은 변경할 수 없는 값이고 (let의 경우에도 변수를 재할당으로 초기화 하고 다른 메모리자리에 값을 넣어 교체하는 것일뿐 직접적인 변경은 불가능하다.), 그렇기에 재할당을 할 수 없는 const는 값을 변경할 방법이 없는것이다!

그렇기에 대부분 const를 상수로 이용할 때에는 변수로 이용할 때와는 다른 상수이름을 사용해주는게 일반적이다.

const A_PERCENTAGE = 0.1; //스네이크 케이스와 대문자로 표현

3-3. const와 객체

const로 선언된 변수는 변경할 수 없다. 다만 const 키워드로 선언된 변수에 객체를 할당하는 경우에는 값을 변경할 수 있다. 객체는 재할당없이 직접 변경이 가능하기 때문이다. (이에 대한 내용은 객체때에 다루겠다…!)

const myInfo = {
  name: "geunhong",
  age: 21,
};

myInfo.age = 25;

console.log(myInfo); // [object Object] {age: 25, name: "geunhong"}

4. 결론

ES6에서는 이제 var를 사용하지 않는다. 중복선언 문제, 스코프 문제, 변수 호이스팅 문제가 있기에 ES6부터는 let과 const를 사용한다. 둘 중 무엇을 사용할까 고민말고 더 안전한 const를 기본적으로 사용하는게 일반적이다. const는 재할당이 금지되어서 의도치 않게 (실수로 같은 변수이름을 사용하는…) 발생하는 실수를 줄여준다. 반드시 재할당이 필요하다면 let으로 변경하여 사용하자.