변수 선언시 var 를 사용하지 않는다. const 와 let 을 사용한다.
왜 우테코 프로그래밍 요구사항에서는 var 를 사용하지말고 const 와 let 을 사용하라고 할까?
그에 대한 이유에 대해 생각해보면서, var 와 let , const 의 차이점을 알아보고 동시에 변수 호이스팅(hoisting)에 대해도 살펴보자
선언 → 초기화 → 할당
실행 컨텍스트: 실행할 코드에 제공할 환경 정보들을 모아놓은 객체
스코프: 스코프란 현재 접근할 수 있는 변수들의 범위
var a; // 선언
a = 'abc' // 변수 a에 데이터 할당
var a ='abc' // 선언과 할당 동시에 한 경우
위의 코드 진행에 따른 메모리 영역의 변화
a라는 이름(식별자)을 가진 변수를 선언하게 되면 (var a;)
,
메모리 영역 속 비어있는 공간의 주소에 해당 변수를 할당해주고 비어있는 값을 가진 상태로 초기화한다.
이 상태에서 해당 변수에 데이터를 할당한 경우 (a = 'abc')
, 메모리 영역 속 빈 공간(주소)에 'abc'라는 데이터를 저장 후 비어있던 변수 a의 값에 해당 주소를 대입해준다.
(만약 메모리 영역의 데이터 영역에 데이터 'abc'가 이미 존재한다면 새로운 데이터를 저장하지 않고 해당 주소를 대입해준다.)
ES6 에서 등장한 let 과 const 가 등장하기 전에는 var 를 사용하는 것 말고는 변수를 선언할 수 있는 방법이 없었다.
하지만 var 가 가지는 높은 자유도의 특징때문에 주의를 기울이지 않을 시 예상치 못한 변수의 변경 및 그에 따른 오류가 발생할 수 있었다.
의도치 않는 변수의 조작이 잦아진다는 것은 단점이 되었고 이에 따라 let 과 const 키워드가 도입되었다.
// 재할당
var a = 1;
a = 2;
console.log(a);
// 실행 결과
2
// 재선언
var a = 1;
var a = 2;
console.log(a);
// 실행 결과
2
이미 변수 a를 생성한 이후에도 해당 변수의 할당 및 선언이 자유롭게 가능하다는 것을 볼 수 있다. 이는 변수 조작이 쉽다는 것을 의미하며,var 의 넓은 스코프를 고려하면 재선언이 가능하다는 것은 의도치 않은 변수 조작이 일어날 수 있다는 것을 의미한다. 이는 다음 특징에서 자세히 살펴보자.
// 재할당
let a = 1;
a = 2;
console.log(a);
// 실행 결과
2
// 재선언
let a = 1;
let a = 2;
console.log(a);
// 실행 결과 : 에러 발생
Uncaught SyntaxError: Identifier 'a' has already been declared
- let 키워드로 변수 생성 시 재선언은 불가능하다.
const a = 1;
a = 2; // 재할당
console.log(a);
// 실행 결과 : 에러 발생
Uncaught TypeError: Assignment to constant variable.
const a = 1;
const a = 2; // 재선언
console.log(a);
// 실행 결과 : 에러 발생
Uncaught SyntaxError: Identifier 'a' has already been declared
- const 키워드의 경우, 재할당과 재선언이 모두 불가능한 상수(constant)를 선언할 때 사용한다. 재할당과 재선언이 불가능하기 때문에 선언과 동시에 할당을 해주지 않으면 오류가 발생한다.
var a = 1;
console.log(a);
{
var a = 2;
}
console.log(a);
// 실행 결과
// 1
// 2
변수 선언과 할당을 의미하는 var a = 2;
가 블록{ }
안에 있음에도 불구하고 블록 밖의 a 의 값이 변경(재할당)이 된 것으로 확인 할 수 있다. 같은 함수 내부에서 선언한 변수에 대해서 의도치 못한 변수 변경이 가능하다는 것을 의미하다. 이를 좀 더 확실히 알아보기 위해 블록 레벨 스코프를 가지고 있는 let
키워드의 경우를 살펴보자.
let a = 1;
console.log(a);
{
let a = 2;
}
console.log(a);
// 실행 결과
// 1
// 1
앞서 살펴보았던 코드와 비교한다면 var 에서 let 으로 변수 키워드만 바뀌었을 뿐인데 실행결과가 다르다.
이는 let 키워드가 블록 레벨 스코프를 가지기 때문인데, let 과 const 처럼 블록 레벨 스코프로 선언된 변수는 코드 블록 내에서만 유효하며 코드 블록 외부에서는 참조할 수 없어 블록 구분에 따른 제한적인 변수 사용이 가능하다.
호이스팅에 대해 알아보기전에 해당 코드의 진행에 대해 예상해보겠다.
// 1번 코드
console.log(a);
var a = '테스트';
console.log(a);
// 2번 코드
console.log(a);
let a = '테스트';
console.log(a);
// 3번 코드
console.log(a);
const a = '테스트';
console.log(a);
위의 1,2,3번 코드를 브라우저에서 실행하게되면 콘솔에 나오는 결과가 1번과 2번,3번의 결과가 조금 다른 것을 확인 할 수있다.
// 1번 코드 실행 결과
undefined
테스트
// 2번 코드 실행 결과
Uncaught ReferenceError: Cannot access 'a' before initialization - 에러발생
// 3번 코드 실행 결과
Uncaught ReferenceError: Cannot access 'a' before initialization - 에러발생
JavaScript에서 호이스팅(hoisting)이란, 작성된 소스코드를 읽어내려가며 실행하는 인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미한다. - Javascript MDN
앞서 설명한 변수 생성 과정에서는, 메모리가 할당되는 초기화의 과정은 변수의 선언 이후 진행되는 것으로 확인했다.
하지만 var 키워드를 읽은 자바스크립트의 인터프리터는 변수의 선언 이전에 미리 변수에 대한 메모리 공간을 미리 할당해두는데, 이때 메모리 공간 속의 값에 대한 데이터를 빈 공간
으로 두는 것이 아니라 undefined
라는 값으로 미리 채워두도록 한다. 이 때문에 var 로 선언한 변수는 선언 전에서부터 undefined 라는 값을 가지고있다.
선언되지 않은 변수를 사용하는 것은 다른 언어들에서는 찾을 수 없는 예외적인 상황이기도 하며, 혼란을 야기할 수 있기 때문에, 호이스팅 과정에서 어떤 값으로도 초기화하지 않는 let 과 const 의 필요성이 야기되었다.
ES5 까지 유일하게 사용되었던 변수 선언 키워드 var
는 높은 자유도를 띄는 특징을 가지고 있었고, 이는 변수의 할당에 관련한 예외적인 상황을 유발시킬 수도 있는 단점으로도 치부되었다. 이에 따라 애초에 변수 선언 단계부터, 변수 처리와 관련한 의도치 않은 상황들을 피할 수 도록 ES6 부터 let
과 const
라는 변수와 상수를 다룰 수 있는 키워드가 등장하였다.
let과 const로 선언한 변수도 호이스팅 대상이지만, var와 달리 호이스팅 시 undefined로 변수를 초기화하지는 않습니다. 따라서 변수의 초기화를 수행하기 전에 읽는 코드가 먼저 나타나면 예외가 발생합니다. - Javascript MDN
let 과 const 도 호이스팅의 대상이다.
let 과 const 의 선언 부분은 호이스팅(선언부 끌어 올리기)되어 상단에서 실행되긴하지만, 초기화까지는 이루어지지 않기 때문에, 코드상의 변수 선언 부 등장 이전에 해당 변수를 사용하면 ReferenceError 를 가지게 되는 것이다.
(var 는 이와달리 선언과 초기화가 같이 이루어지며 이때 앞선 설명과 같이, undefined 라는 값으로 초기화됨)
function do_something() {
console.log(bar); // undefined
console.log(foo); // ReferenceError
var bar = 1;
let foo = 2;
}
do_something 이라는 함수가 호출되었을때, let foo = 2;
라는 변수 선언부가 실행되어 비로소 해당 변수에 접근이 가능하기 바로 그 이전까지의 부분을 TDZ(Temporal Dead Zone)
이라고 부른다.
function do_something() {
/// TDZ ///
console.log(bar); // undefined
console.log(foo); // ReferenceError
var bar = 1;
/// TDZ ///
let foo = 2;
}
편의상 , 코드의 작성 순서(위치)로 TDZ를 표시했지만 TDZ는 시간상(일시적) 사각지대
로 코드의 실행 순서(시간)에 의해 형성된다.
이번 학습 로그에서는 변수의 호이스팅에 대해서만 다루었지만 function 을 이용한 함수 선언 호이스팅에도 알아보고
const a = () => {};
function a() {}
위 두 함수의 차이에 대해서 생각해보는 것도 좋을 것 같다.