[ES6] 1. let과 const

자바스크립트의 실행

자바스크립트 파일이 브라우저에서 해석될 때, 해당 자바스크립트 파일이 얼마나 많은 메모리를 차지하게 될지 계산하는 과정을 거친다.

즉, 자바스크립트 파일의 메모리를 확보하는 과정을 거친다.

이 과정에서 모든 자바스크립트 변수는 호이스팅 과정을 거친다.

그렇다면 변수가 선언됐을 때 자바스크립트에서 어떤 과정이 일어나는지 살펴보자.

선언

값을 할당하지 않고 선언만 하는 과정을 거친다.

var a;에서 자바스크립트 엔진은 데이터의 공간을 만들고 그 공간에 a라는 이름을 붙인다.

초기화

등록된 변수를 위한 공간을 메모리에 확보한다. 이 단계에서 변수는 undefined로 초기화된다.

할당

undefined로 초기화된 변수에 값을 할당하는 과정이다.

var a = '';에서 a라는 변수에 ”를 할당한다고 한다.

let

호이스팅

많은 개발자들이 착각하고 있는 부분이 있다.

let으로 선언된 변수는 호이스팅 과정을 거치지 않는다’ 라는 것이다.

하지만 이것은 틀린 말이다.

자바스크립트 파일의 메모리를 할당하는 과정이 필요하기 때문에 호이스팅이 발생한다.

let으로 선언만 되고 할당되지 않은 변수를 사용하려고 하면 var와 달리 Reference Error가 발생한다.

이는 let으로 선언된 변수는 할당 되기 전에 TDZ(Temporal Dead Zone), 일시적 사각 지대에 빠지게 되기 때문이다.
이 때문에 Reference Error가 발생하는 것이다.

ES6

//ES5(var)
console.log(a); //undefined

var a = 1;

console.log(a); //1
//ES6(let)
console.log(b); //Reference Error: b is not defined

let b = 1;

console.log(b);

위의 예제에서 볼 수 있듯이 let으로 선언한 변수를 그 이전에 사용하려고 하면 Reference Error가 발생한다. 이를 보면 호이스팅이 발생하지 않는다고 보일 수 있다.

하지만, 호이스팅이 일어나 메모리 공간을 확보한 뒤, 일시적 사각지대(TDZ) 에 빠져있는 상태이기 때문에 Reference Error가 발생하는 것이다.

블록레벨 스코프

var함수레벨 스코프라면 let블록레벨 스코프를 지원한다.

즉, var로 선언한 변수는 함수 내에 선언하지 않고 iffor와 같은 블록 내에서 선언하면 전역(window) 스코프에 할당된다.

반면, let으로 선언한 변수는 iffor와 같은 블록 내에서 선언을 하면 전역이 아닌 해당 블록 내의 스코프에 할당된다.

//ES5(var)
if(true) {
    var result = 'helloWorld';
}

console.log(result); //helloWorld
//ES6(let)
if(true) {
    let result = 'helloWorld';
}

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

if문 내에서 let으로 선언한 변수는 var와 다르게 블록 내의 지역 변수로 할당 되기 때문에 블록 외부에서는 이를 호출하면 Reference Error가 발생하게 된다.

변수 중복 선언 불가

let으로 선언한 변수는 중복 선언이 불가하다.

var로 선언한 변수는 같은 레벨에서 재할당이 가능하지만, let으로 선언한 변수를 같은 레벨 내에서 다시 선언하면 에러가 발생한다.

//ES5(var)
var result = 'hello';
var result = 'world';

console.log(result); //world
//ES6(let)
let result = 'hello';
let result = 'world'; 

//Uncaught SyntaxError: Identifier 'result' has already been declared

클로저(Closure)

var를 사용할 때 보다 let을 사용할 때 직관적이고 편하다고 생각할 수 있는 대표적인 예가 루프안에서 클로저를 구현할 때다.

//ES5(var)
function count(number) {
    for(var i=1; i <= number; i++) {
        (function (j) {
            setTimeout(function(){
                console.log(j);
            }, i*1000)
        }(i))
    }
}

count(4);
/*
1
2
3
4
/*

즉시 실행 함수를 실행시켜 루프의 i 값을 j에 복사하고 setTimeout()함수에서 사용했다.
이 때 j는 상위스코프의 자유변수이므로 그 값이 유지된다.

위처럼 구현하는 이유는

function count(numberOfCount) {
    for(var i=1; i <= numberOfCount; i++) {
        setTimeout(function(){
            console.log(i);
        }, i*1000)
    }
}

count(4);
/*
5
5
5
5
*/

이와 같이 구현했을 때 결과는 예상과 다르게 5가 4번 1초 간격으로 출력되기 때문이다.

for 루프의 초기문에서 사용된 변수는 함수 레벨 스코프로 인해 전역 변수로 할당되기 때문에 문제가 발생한다.

//ES6(let)
function count(numberOfCount) {
    for(let i=1; i <= numberOfCount; i++) {
        setTimeout(function(){
            console.log(i);
        }, i*1000)
    }
}

count(4);
/*
1
2
3
4
*/

하지만 let블록 레벨 스코프를 가지기 때문에 변수 i는 for문 블록 내의 지역 변수로 할당이 된다.

따라서 위와 같이 간단하게 원하는 결과를 출력할 수 있다.

const

재할당 불가

const는 상수 즉, 변하지 않는 값을 선언하기 위해 사용한다.

이에 따라 const로 선언된 변수는 varlet으로 선언된 변수처럼 재할당이 불가능하다.

const FOO = 123;

FOO = 234;
//Uncaught TypeError: Assignment to constant variable.

위 처럼 const로 선언된 변수에 다른 값을 재할당하려하면 에러가 발생한다.

블록레벨 스코프

const로 선언된 변수 또한 let으로 선언된 변수처럼 블록 레벨 스코프를 가진다.

객체의 할당

const로 선언된 변수는 위에서 말한 것처럼 재할당이 불가능하다.

하지만, const로 선언된 변수의 값이 객체로 할당이 된 경우, 객체 자체를 재할당하는 것은 불가능하지만 객체의 프로퍼티 값은 보호되지 못한다.

const Developer = {
    name : 'BKJang',
    age : 25,
    lang : 'Javascript'
}

Developer.lang = 'Java';

console.log(Developer); //{name: "BKJang", age: 25, lang: "Java"}

Developer = {
    name : 'BKJang',
    age : 25,
    lang : 'Java'
}
//Uncaught TypeError: Assignment to constant variable.

Reference

Published 24 Nov 2018

BKJang's Devlog