
Javascript에는 변수를 선언하는 방법이 세가지가 있다. var와 let,const가 있는데 let,const는 ES6에 처음 나온 변수 선언 키워드로 기존의 var 키워드가 가지고 있는 몇가지 문제점들을 해결하기 위해 등장했다. 어떤 문제점과 차이가 있는지 알아보려 한다.
우선 var,let,const의 차이점을 알아보기 전에 스코프에 대해 알 필요가 있다.
스코프는 쉽게 말해 변수를 참조할 수 있는 범위를 말한다. 스코프의 종류는 전역스코프,함수스코프,블록스코프,렉시컬스코프,모듈스코프,eval스코프가 있다. 하지만 이 글에선 전역,함수,블록스코프를 통해서만 설명하려 한다.
const a = 1;
console.log(a);
function b() {
console.log(a);
const b = 2;
function(c) {
console.log(b);
const c = 3;
}
c();
}
for (let i=0;i<5;i++) {
console.log(a+i);
}
이와 같은 코드가 있을 때 a는 전역스코프에 속하며 b와 c는 함수스코프, for 문의 i는 블록스코프에 속한다.
전역스코프에서 선언된 a는 함수스코프,블록스코프 어느 곳에서든 참조가 가능하다. 함수스코프 안에 있는 변수는 함수스코프 내에서만 참조가 가능한데, 또 함수 내에 중첩되어 있는 함수 c에서 선언한 변수는 외부에 있는 함수인 b에선 참조가 불가능하다. 여기까진 var와 const,let이 동일하다.
하지만 if문,for문 등과 같은 문법 내에서 선언된 변수는 기존의 var키워드로 선언할 경우 외부에서 참조가 가능했다.
for(var i=0;i<5;i++) {
const a = i;
}
console.log(i);
// 출력결과:
// 5
그 이유는 var키워드는 블록스코프를 지원하지 않기 때문이다. 만약 if문,for문등과 같은 블록 안에서 var키워드로 변수가 선언될 경우 이는 해당 블록과 가장 가까운 외부의 함수 또는 전역 스코프에 포함된다.
let과 const는 이러한 문제를 해결해서 블록 내에 있는 변수를 외부에서 참조할 수 없도록 블록스코프를 지원한다.
console.log(a);
var a = 1;
// 출력 결과:
// undefined
console.log(a);
const a = 1;
// 출력 결과:
// Uncaught ReferenceError: a is not defined
자바스크립트 코드는 몇가지 특별한 상황을 제외하면 위에서 아래로 순서대로 실행된다. 그렇기 때문에 첫번째 코드처럼 a가 선언되기 전에 참조되면 에러가 발생할 것으로 예상되지만 결과는 undefined가 출력된다.
이와 같이 변수가 선언되기 전에 참조가 가능한 현상을 호이스팅이라 한다. let과 const는 이러한 호이스팅 현상을 해결해서 아래 코드처럼 const로 선언한 변수를 먼저 참조할 경우 에러가 발생한다.
(정확히 말하자면 let과 const 역시 호이스팅되지만 참조 시 에러가 발생한다는 말이 맞다.)
그렇다면 호이스팅이 발생하는 원리는 무엇일까? 이는 자바스크립트 엔진이 자바스크립트 코드를 실행하는 과정과 관련이 있다. 자바스크립트 엔진은 코드를 실행할 때 두가지 과정을 거친다.
코드 평가란 코드에서 선언된 변수나 함수등의 식별자를 선언하는 과정을 거친다.
그리고 코드 실행 과정에서 변수들에 값을 할당하거나 함수를 실행하는 등의 과정을 거치는 것이다.
하지만 var키워드로 선언된 변수는 코드 평가 과정에서 식별자를 선언할 뿐 아니라 undefined가 할당되기 때문에 실행 단계에서 아직 값이 할당되지 않아도 참조했을 때 에러가 발생하지 않는 것이다. 이것이 호이스팅된 변수를 참조했을 때 undefined가 반환되는 이유이다.
let,const키워드로 선언한 변수는 코드 평가 과정에선 동일하게 식별자가 선언되지만 여기에 undefined를 할당하지 않고 코드 실행 과정에서 코드에 따라 값이 할당된다. 이를 TDZ(일시적 사각지대)라 부른다.
var 키워드의 특징 중 하나로 동일한 변수명으로 재선언이 가능하다는 점이다.
var a = 1;
var a = 2;
console.log(a);
// 출력 결과:
// 2
let b = 1;
let b = 2;
console.log(b);
// 출력 결과:
// Uncaught SyntaxError: Identifier 'b' has already been declared
첫번째 코드의 결과를 보면 알 수 있듯이 var 키워드로 선언한 a변수는 또 다시 선언했을 때 에러가 발생하지 않고 정상적으로 2가 할당되는 것을 알 수 있다.
하지만 let 키워드로 b라는 이름의 변수를 선언한 다음 다시 b라는 이름으로 변수를 선언할 경우 에러가 발생한다.
위와 같이 코드가 짧다면 동일한 변수명으로 선언할 경우 알아차리기 쉽지만 코드가 길어진다면 이를 확인하기 어렵다. 만약 a변수를 단일한 목적으로 사용하고 값을 재할당하기 위한 작업이었다면 문제가 생기지 않지만, 기존에 a라는 변수를 사용하는 기능1이 있는 상태에서 새로운 기능2에서 사용하기 위해 변수 a를 새로 선언하고 값을 할당한다면 개발자가 의도한 바와 다르게 작동할 가능성이 크다.
재선언이 가능할 경우 이러한 문제가 있기 때문에 let과 const는 재선언이 불가능하게 만들어졌다.
프로그램이 작동하는 중 값이 변경되면 안되는 경우가 있다. 하지만 var 키워드로 선언한 변수에는 값을 재할당하는 것을 막을 수 없는데, 이를 위해 값을 재할당하는 것이 가능한 변수를 생성하는 let키워드와 처음 할당된 값을 재할당할 수 없는 상수를 만드는 const키워드를 통해 변수와 상수 선언 방법을 구분했다.