La foret rouge
Published on

JavaScript의 var, let, const 비교

Authors
  • avatar
    Name
    신주용

변수 선언을 위한 키워드 var, let, const

자바스크립트에서는 변수 선언에 사용할 수 있는 키워드가 3가지 있습니다. 바로 var, let, const입니다. 이 중 var는 기존에 있던 키워드이고 let, const는 ES6에 새롭게 추가된 키워드입니다. 세 키워드 모두 변수 선언에 사용되지만, 범위(scope)와 동작이 약간씩 다릅니다. 어떤 점에서 차이를 보이는지 알아보겠습니다12.

변수 범위 (Scope)

var는 함수 내에서 선언될 떄는 함수 범위, 함수 밖에서 선언될 떄는 전역 범위로 동작. 그러므로 함수 안의 if문과 같이 한 단계 더 안에서 var 변수를 선언한다고 생각해 봅시다. var 변수는 if문 밖이더라도 동일한 함수 안이라면 변수에 접근이 가능합니다.

function a() {
  {
    var b = 1;
  }
  console.log(b); // 가능. 1 출력
}

반면, let, const는 블록 범위입니다. 같은 함수 안이더라도 내부 블록에서 선언했다면 해당 블록 밖에서는 접근이 불가합니다.

function a() {
  {
    let b = 1;
  }
  // console.log(b); // ReferenceError: Can't find variable: b
}

초기값

var, let은 선언 시 초기화를 안 한다면 기본적으로 undefined로 초기화됩니다.

> var a;
< undefined
> let b;
< undefined

const를 사용할 때는 무조건 선언 시 초기값을 할당해줘야 합니다.

> const a;
< SyntaxError: Unexpected token ';'. const declared variable 'a' must have an initializer.

호이스팅 (Hoisting)

var는 함수나 전역 범위의 제일 위로 호이스팅 됩니다. 다만, 변수의 선언부만 호이스팅 되고 초기값은 자동으로 undefined가 들어가게 됩니다. 그러므로 이렇게 사용할 때는 동작에 주의가 필요합니다. 예상치 못한 값이 들어가 있을 수 있기 때문입니다.

function a() {
  // var b = undefined; // 실제 코드 아님. 내부적으로는 호이스팅 되어 동작. undefined로 초기화.
  console.log(b); // 호이스팅 되었으므로 가능. undefined 출력.
  var b = 1;
  console.log(b); // 1
}

let, const도 호이스팅이 되기는 하지만, var와는 달리 호이스팅 때는 자동으로 초기화되지 않습니다. 그러므로 이 때 변수 사용을 시도하면 ReferenceError가 발생합니다.

function a() {
  // let b; // 실제 코드 아님. 내부적으로는 호이스팅 되어 이렇게 선언된 것처럼 동작. 초기화는 안 됨.
  console.log(b); // ReferenceError: Cannot access uninitialized variable.
  // 호이스팅 되었으므로 a라는 변수가 있는건 알지만 초기화 안되어 에러 발생.
  let b = 1;
}

변수에 값 재할당

var는 재선언, 재할당이 가능합니다. 특히 재선언이 가능한 점이 특이한데, 호이스팅 되어 함수 범위의 최상단에 선언이 이미 된 것처럼 동작하기 때문에 가능합니다.

function a() {
  // var b = undefined; // 실제 코드 아님. 함수 범위 최상단으로 호이스팅 되어 동작. undefined로 초기화.
  {
    var b = 1; // b를 1로 초기화. b = 1; 과 동일한 동작.
    var b = 2; // b에 2를 재선언. b = 2; 와 동일.
  }
  var b = b + 1; // b를 최초 선언한 부분은 내부 블록 안이라 자바 같은 언어에서는 오류가 나겠지만,
  // JS에서는 호이스팅되어 함수 최상단에 선언된 것처럼 동작하므로 문제 없음.
  console.log(b); // 3
}

let은 재할당은 가능하지만, 동일 범위 내에서 재선언은 불가합니다.

function a() {
  {
    // let b; // 실제 코드 아님. 블록 범위 최상단으로 호이스팅 되어 동작. 초기화 없음.

    let b = 1; // b를 1로 초기화. b = 1; 과 동일한 동작.
    // let b = 2;     // SyntaxError: Cannot declare a let variable twice: 'b'.
    // 동일 범위에서 재선언 불가.
    b = 2; // 재할당은 가능.
    console.log(b); // 2
  }
  let c = b + 1; // ReferenceError: Can't find variable: b
  // b를 초기화한 부분은 내부 블록이므로 여기서는 알 수 없는 변수.
}

const는 재선언, 재할당이 모두 불가능합니다.

function a() {
  {
    // const b; // 실제 코드 아님. 블록 범위 최상단으로 호이스팅 되어 동작. 초기화 없음.

    const b = 1; // b를 1로 초기화. b = 1; 과 동일한 동작.
    // const b = 2;     // SyntaxError: Cannot declare a let variable twice: 'b'.
    // 동일 범위에서 재선언 불가.
    // b = 2;             // TypeError: Attempted to assign to readonly property.
    // 재할당도 불가.
  }
  const c = b + 1; // ReferenceError: Can't find variable: b
  // b를 초기화한 부분은 내부 블록이므로 여기서는 알 수 없는 변수.
}

통합 테스트

위 내용을 종합적으로 테스트 해보는 코드와 실제 실행 결과는 다음과 같습니다.

function a() {
  // var m = undefined;                 // hoisted. not real code, just for explanation.
  // var m of ln 7 and ln 9. declared and initialised.
  console.log("before block: ", m); // before block: undefined

  {
    var m = "a: 1";

    var m = "a: 2"; // able to redeclare
    console.log("in block: redeclare: ", m); // in block: redeclare "a: 2"

    m = "a: 3"; // able to reassign
    console.log("in block: reassign: ", m); // in block: reassign: "a: 3"
  }
  console.log("after block: ", m); // after block: "a: 3"
  // because var is function-scope
}

a();
console.log("out of fn a: ", m); // ReferenceError: Can't find variable: m
function b() {
  {
    // let n;                                       // hoisted. not real code, just for explanation.
    // var m of ln 7 and ln 9. declared but not initialised.
    // console.log("before block: ", n);  // ReferenceError: Can't find variable: n

    let n = "b: 1";

    // let n = "b: 2";                               // SyntaxError: Cannot declare a let variable twice: 'n'.
    console.log("in block: redeclare: ", n); // in block: redeclare: "b: 1"

    n = "b: 3"; // able to reassign
    console.log("in block: reassign: ", n); // in block: reassign: "b: 3"
  }
  // console.log("after block:", n);               // ReferenceError: Can't find variable: n
  // because var is block-scope
}

b();
// console.log("out of b:", n);  // ReferenceError: Can't find variable: n
function c() {
  {
    // const o;                                  // hoisted. not real code, just for explanation.
    // var m of ln 7 and ln 9. declared but not initialised.
    // console.log("before block:", o);  // ReferenceError: Can't find variable: o

    const o = "c: 1";

    // const o = "c: 2";                            // SyntaxError: Cannot declare a const variable twice: 'o'.
    console.log("in block: redeclare: ", o); // in block: redeclare: "c: 1"

    // o = "c: 3";                                    // TypeError: Attempted to assign to readonly property.
    // console.log("in block: reassign: ", o);
  }
  // console.log("after block:", o);              // ReferenceError: Can't find variable: o
  // because const is block-scope
}

c();
// console.log("out of c:", o);  // ReferenceError: Can't find variable: o

Prerequisites: 이 글에서 언급되었으나 깊게 설명하지 않은 내용입니다.

  • 변수 호이스팅 (Hoisting)
  • TDZ(Temporal Dead Zone)

Footnotes

  1. Sarah Chima Atuonwo. "Var, Let, Const의 차이점은?" freecodecamp.org. https://www.freecodecamp.org/korean/news/var-let-constyi-caijeomeun/ (accessed Sep. 07, 2023).

  2. 인파. "📚 var / let / const 차이점 정리 (변수 호이스팅)." Inpa Dev 👨‍💻💻. https://inpa.tistory.com/entry/JS-📚-var-let-const-차이점-변수-호이스팅 (accessed Sep. 07, 2023).