All Articles

var let const, 그리고 호이스팅

var let const, 그리고 호이스팅

var

우리가 모두 아는 var 키워드는 아래와 같은 특징을 가지고 있다.

  1. 함수레벨 스코프를 가지고 있다.

    대부분의 프로그래밍 언어들이 블록 레벨 스코프를 사용하고 있지만, var로 선언된 키워드는 함수레벨 스코프를 갖는다.

    var name = 'hello'
    
    function t() {
        var name = 'hi'
        console.log(name) // hi
    }
    
    t()
    console.log(name) // hello
  2. var 키워드는 생략이 가능하다.

    생략이 가능하기 때문에, 함수가 선언한 환경의 this에 영향을 받는다. 일반적인 웹 환경에서는 window일 것이다.

  3. 중복 선언이 가능하다.

    var name = 'hello'
    var name = 'hi'
    console.log(name) // hi
  4. 호이스팅 당한다.

    호이스팅이란

    스코프 안에 있는 선언들을 모두 스코프의 최상위로 끌어올리는 것

    이다. 자바스크립트 인터프리터가 코드를 해석할 때 함수의 선언, 할당, 실행을 모두 나눠서 처리하기 때문이다. 예를 들어보자.

    console.log(name) // undefined
    var name = 'hello'

    이 코드는 참조 에러가 나지 않고, undefined를 리턴한다. 그 이유는 자바스크립트가 호이스팅을 하면서 아래와 같은 방식으로 코드를 해석하기 때문이다.

    var name // undefined
    console.log(name)
    name = 'hello'

2. let

let은 es6에서 가장 잘 알려진 기능 중 하나다. var랑 비슷한 것 같지만, 사실 몇가지 다른 점이 있다.

  1. 블록 수준 스코프를 가지고 있다.

    아래의 예를 살펴보자.

    {{{var name = 'hello'}}}
    console.log(name) // hello
    
    {{{let name2 = 'hi'}}}
    console.log(name2) // ReferenceError: name2 is not defined

    letconst는 모두 블록 수준의 스코프를 사용 하고 있다. 따라서 블록 내부에서 선언한 변수는 모두 지역변수로 취급된다. 때문에 블록 내부에서 선언한 변수들을 참고할 수가 없다.

  2. 키워드 생략이 불가능하다.

    키워드를 생략하면 var처럼 작동하게 된다.

  3. 중복선언이 불가능하다.

    그렇다.

  4. 호이스팅 당한다.

    가끔 인터넷을 뒤지다보면, letconst는 호이스팅 당하지 않는다는 이야기가 있는데, 이는 잘못된 사실이다. 두 키워드 모두 호이스팅 당하기는 마찬가지이다.

    이것을 이해하기 위해서는, Temporary Dead Zone에 대해 알아야 한다.

Temporary Dead Zone

아래 코드를 살펴보자.

name = 'hello' // ReferenceError: Cannot access 'name' before initialization
let name = 'hi'

자. 만약 name이 호이스팅 되지 않았다면, 두 번째 let선언에서 already declared에러가 났었어야 했다. 근데 현실은 초기화 하기전에 엑세스 할 수 없다는 에러를 내뱉었다.

아래 코드는 어떨까?

function sayHello() {
  return name
}
let name = 'hi'
console.log(sayHello()) // hi

별탈없이 hi를 내뱉은 것을 볼 수 있다. 그 말인 즉, name이 분명 호이스팅 되었다는 뜻이다.

다른 이야기로 넘어가서, 자바스크립트에서는 총 3단계에 걸쳐서 변수를 생성한다.

  1. 선언(Declaration): 스코프와 변수 객체가 생성되고, 스코프가 변수 객체를 참조한다.
  2. 초기화(Initialization): 변수 객체 값을 위한 공간을 메모리에 할당한다. 이 때 할당되는 값은 undefined다.
  3. 할당(Assignment): 변수 객체에 값을 할당한다.

var는 선언과 동시에 초기화가 이루어진다. 즉, 선언과 동시에 undefined가 할당된다.

그러나 letconst는 다르다. 선언만 될뿐, 초기화가 이루어지지 않는다. 바로 이단계에서 TDZ에 들어가게 되는 것이다. 즉, 선언은 되어있지만, 초기화가 되지 않아 이를 위한 자리가 메모리에 준비되어 있지 않은 상태라는 것이다.

const

const에서 구별되는 특징만 몇가지 살펴보자.

  1. const는 초기화와 동시에 선언이 이루어져야 한다.

    let hello
    hello = 'hello'
    
    const hi //SyntaxError: Missing initializer in const declaration
    hi = 'hi'
  2. const 자체가 값을 불변으로 만드는 것이 아니다.

    아래 코드는 당연히 안된다.

    const hello = 'hello'
    hello = 'hi' // TypeError: Assignment to constant variable.

    그렇다고 이것도 안되는 것은 아니다.

    const hello = ['hi']
    hello.push('hello')
    const hello = 'hello'
    var hi = hello
    hi = 'hi'
    console.log(hello) // hello
    console.log(hi) // hi

    객체 자체를 동결시키기 위해서는 Object.freeze를 사용하면 된다.

  3. 상수를 선언할 때 사용한다.

결론

  • 호이스팅은 변수의 선언을 최상단에 끌어올린다는 뜻이다.
  • var let const는 모두 호이스팅 된다.
  • let 선언과 동시에 TDZ에 들어가서 초기화가 필요한 별도의 상태로 관리된다.
  • const 는 선언과 동시에 초기화, 할당까지 이루어진다.
  • var는 왠만하면 쓰지말자
  • let대신 const를 사용하자. let으로 선언되어 있다면, 어디선가 이 변수가 바뀔지도 모른다는 생각을 가지고 있어야 하므로, 코드를 읽기가 어려워진다. 반면 const는 초기화, 선언, 할당까지 되어 있으니 변경되지 않을 것이다라는 확신으로 코드를 볼 수 있다. (물론 객체의 속성은 바뀔 수 있음.)