관련지식
javascript, var, const, let, array, hoisting

이번엔 변수를 선언할때 사용하는 키워드 몇개를 설명하려고 합니다. 전통적으로 많이 사용해온 var 키워드와 나중에 추가된 constlet 키워드가 있는데 어떻게 다를까요?

아래와 같은 소스가 있다고 가정하겠습니다.

  1. function define() {
  2. var a = 10;
  3. const b = 20;
  4. let c = 30;
  5. console.log('second', a, b, c);
  6. }
  7. define();

const

먼저 아래 const 로 선언한 변수를 재정의 해보겠습니다.

  1. function define() {
  2. var a = 10;
  3. const b = 20;
  4. let c = 30;
  5. console.log('second', a, b, c);
  6. //추가
  7. try {
  8. b = 30;
  9. }
  10. catch(e) {
  11. console.log(e);
  12. }
  13. }
  14. define();

try...catch 로 묶은것에서 짐작되겠지만, 이 코드는 ‘TypeError: Assignment to constant variable.’ 오류가 납니다. const 로 선언된 변수는 한번 정의된 값을 다시 재정의 하는것이 불가능합니다. 따라서 다른 변수와 다르게 값을 정의하지 않는 변수 선언이 불가능합니다.

  1. const KEY1 = 'name'; //정상
  2. const KEY2; //Missing initializer in const declaration 오류

var

var 키워드를 이해할때 반드시 필요한 개념이 호이스팅(Hoisting) 이죠. 이전에 ‘[javascript] scope와 호이스팅(hoisting)’ 에서 다루었던 것처럼 var 키워드로 선언한 변수는 호이스팅이 발생합니다. 아래 코드를 추가해보겠습니다.

  1. function define() {
  2. console.log('first', a); //추가
  3. var a = 10;

만약 자바 코드였다면 위 소스는 오류가 나겠지만 자바스크립트 에서는 undefined 가 출력됩니다. 그리고 변수 b와 c를 사용할 경우엔 오류가 납니다. const 와 let 키워드로 선언한 변수는 호이스팅이 발생하지 않기 때문입니다.

참고

  1. console.log('first', a); //first undefined 출력
  2. console.log(c); //ReferenceError: c is not defined

undefined 와 ReferenceError: c is not defined 은 의미가 비슷해보이지만 실제로는 큰 차이가 있습니다. undefined 는 참조할 대상은 있지만 정의된 값이 없는 상태, ReferenceError는 참조할 변수 자체가 없는 것입니다.

let

호이스팅이 없으면 무엇이 달라질까요?

  1. var a = 10;
  2. const b = 20;
  3. let c = 30;
  4. ...
  5. var a = 50; //추가
  6. let c = 60; //추가

같은 함수 안에서 밑에쪽에 동일한 이름을 재선언하는 코드를 추가하였습니다. 실행하면 어떻게 될까요? ‘var a = 50;’ 은 별다른 이상이 없지만 ‘let c = 60;’ 에서는 오류가 납니다. var 키워드는 호이스팅으로 인해 변수 선언부가 스코프의 최상위 한곳으로 정리되지만, let은 그렇지 않죠. 따라서 중복 선언으로 오류가 발생합니다.

다음 소스를 보겠습니다.

  1. for(var i = 0; i < 3; i++) {
  2. }
  3. for(let j = 0; j < 3; j++) {
  4. }
  5. console.log(i); //3 출력
  6. console.log(j); //ReferenceError

var로 선언한 변수 i는 호이스팅으로 인해 함수 스코프만 적용됩니다. 하지만 let 으로 선언한 변수는 호이스팅이 없기 때문에 지역 스코프가 적용됩니다. 자바에서 int로 선언했을때와 똑같은 효과가 나타났네요!

Lexical Scope

이번엔 아래와 같이 소스를 추가해보겠습니다.

  1. var d = 'a'; //추가
  2. const e = 'b'; //추가
  3. let f = 'c'; //추가
  4. function define() {
  5. console.log('first', a);
  6. ...
  7. d = 'd'; //추가
  8. e = 'e'; //추가
  9. f = 'f'; //추가
  10. }

define() 함수 안에서 d, e, f 변수의 값을 할당하려고 하지만 동일한 함수 스코프 내에 선언된 변수가 없습니다. 따라서 상위 Lexical Scope 에서 변수를 찾게 되는데, 변수 e는 const로 선언되어 있네요. 따라서 “e = ‘e’;” 코드는 오류가 발생합니다. 이번엔 소스 위치를 조금 바꿔보겠습니다.

  1. function define() {
  2. ...
  3. d = 'd';
  4. f = 'f';
  5. }
  6. define();
  7. var d = 'a'; //이동
  8. let f = 'c'; //이동

변수에 ‘d’, ‘f’ 값을 할당하려고 할때 상위 스코프의 변수를 찾는것은 동일합니다. 그런데 변수의 선언 위치가 define() 실행 이후로 옮겨졌네요. 이 소스는 아래와 같이 동작합니다.

  1. var d; //호이스팅
  2. function define() {
  3. ...
  4. d = 'd';
  5. f = 'f'; //ReferenceError
  6. }
  7. define();
  8. d = 'a'; //이동
  9. let f = 'c'; //이동

변수 d는 사용하는데 문제 없지만 변수 f는 호이스팅이 발생하지 않기 때문에 define() 함수 안에서는 참조 할수가 없습니다.

정리

cosnt 키워드는 그 특징이 뚜렸하지만 varlet은 비슷하면서 다른 부분이 많아서 처음 접할때 헷갈릴수 있습니다. 특징을 정확히 알아두셔야 코딩을 하면서 자잘한 실수를 줄일수 있을것 같네요.