초창기 자바스크립트의 변수는 var 하나뿐이었습니다.
var는 variable의 줄임말로. 이름 그대로 변수를 선언할 때 쓰는 키워드입니다.
var a = 0;
var b = function() {
return console.log('function!');
}
많이 보셨으리라 생각됩니다. 하지만 var는 자바스크립트로 코딩하는데 가끔씩 빡치는 상황을 연출하곤 했습니다.
바로 Hoisting 때문입니다.
Hoisting이란 일반적으로 선언된 함수를 코드 상단으로 올리는 것으로 설명하곤 합니다.
console.log("1",name);
function sayHi() {
console.log("2",name);
var name = 'Kang';
console.log("3",name);
}
sayHi();
이런 코드가 있습니다. 과연 console.log에 찍힐 결과는 무엇일까요?
정답은!!
여기서 의문이 생깁니다. 첫 줄의 console.log(name)은 왜 비어있는가?
이 의문에 대한 고민은 이러한 생각으로 이어집니다
"자바스크립트는 선언되지 않은 변수를 사용할 때 아무것도 안 찍어주는가?"
하지만 그것도 아닙니다.
첫 줄에 선언되지 않았던 age를 호출하니 정의되지 않았다고 터져버립니다.
여기서 이제 Hoisting이 나옵니다.
자바스크립트 var 변수는 자바스크립트가 위에서부터 실행되기 전에 우선적으로 코드에 있는 var 변수들을 가져와 먼저 선언하고 초기화해버립니다.
그래서 위 코드 안에 var name = 'Kang'이 있기 때문에 var name를 우선적으로 가져와 선언하고 undefined로 초기화를 시켜버립니다. (하지만 크롬상에서 돌려 봤을 땐 undefined 대신에 아무것도 안 뜹니다)
그다음에서야 한 줄 한줄 코드를 읽어가며 var name ='Kang' 줄에 도달했을 때
undefined로 초기화돼있던 name를 Kang으로 값을 변경하는 것이죠. 그래서 undefined, Kang, undefined가 나오게 되는 게 일반적입니다.
이제 진짜 문제는 지금부터입니다. Hoisting을 보면 일견 편해 보입니다.
사용하기 전에 우선적으로 초기화를 해주니 훨씬 안전하게 값일 할당해서 사용할 수 있어 보입니다.
하지만 사용자가 의도치 않는 결과를 내기도 합니다. 선언-> 할당-> 사용의 순서가 일반적인 흐름인데
사용-> 선언-> 할당의 순서가 되어도 코드상으로는 문제가 보이지 않기 때문입니다.
실제 실무에서도 로직은 돌아가는데 var로 인해 엉뚱한 시점에서 값이 할당되어 돌아가는 경우가 있습니다.
자바스크립트가 오류도 주지 않고 돌아가기 때문에 문제를 한참 들여다봐야 하고 또한 var는 { } 크기가 아니기 때문에 더더욱 어렵게 만듭니다.
이러한 불편함을 해소하기 위해서
ECMA6( ECMAScript 2015 )에서 let, const 키워드가 추가되었습니다.
let과 const 가 var와 가장 다른 점은 Scope, 즉 영역입니다.
이전의 var의 경우 function 단위의 영역을 가지고 있었습니다.
사진으로 보시다시피 for문 안에 변수 test를 선언하였는데, for문 밖에서도 test가 유지되는 것을 볼 수 있습니다. 이는 우리가 일반적으로 사용하던 C, Java와는 너무 상이하고, 디버깅을 어렵게 하는 요소로 작용하였습니다.
또한 자바에서 사용하던 final과 같은 키워드가 없어 변하지 않는 상수값으로 약속(코드 컨벤션)하고 이를 최대한 신경 써서 코딩하는 상황이 생겨버렸습니다. ( 일반적으로 대문자로 표시했었음)
var API_KEY = '1219231fm2331';
API_KEY = 'changed'; // <- Oops!!!
이런 불편함을 해소하기 위해
final과 같은 역할의 const와, block 영역 단위의 let 이 나오게 되었습니다.
그럼 이제 let과 const에 대해 살펴보겠습니다.
이전과 달리 for문 안에서 선언한 test가 외부에선 not defined 된 걸 확인할 수 있습니다. Scope가 작아졌으니 훨씬 디버깅과 컨트롤하기가 쉬워졌습니다.
그다음은 const입니다.
보시다시피 'constant variable에 assign 했다'라고 알려주며 에러를 뱉어버립니다. 안된다는 거죠.
그렇다면 const는 변하지 않는다는 것을 의미하는가? 답은 그렇지 않습니다.
const list = ['a','b','c'];
list.push('d');
console.log(list) // ["a", "b", "c", "d"]
list 배열에 값 push 하게 되면 들어갑니다. 객체와 똑같습니다.
따라서 const 키워드의 의미를 좀 더 자세히 하자면 '재할당하지 않는다'가 더 정확합니다.
그렇다고 자바스크립트 Hoisting은 어떻게 되는 것인가?
코드를 한번 보겠습니다.
name의 경우 위에서 공부 한때로 Hoisting 되어 undefined로 초기화시킨 후에 사용합니다.
근데 let으로 선언된 age의 경우 가차 없이 ReferenceError를 뱉어버립니다.
에러 로그 그대로 Cannot access 'age' before initalization입니다.
네 그렇습니다. let, const의 경우 Hoisting으로 위로 끄려올려지지만 초기화가 되지 않고 이때 접근이 불가능한 상태가 됩니다.
이미지를 한 장 더 보겠습니다.
위에 로그와 비슷하면서도 다르다는 것을 알 수 있습니다.
위의 사진에서는 age는 초기화되기 전까지 사용할 수 없다고 되어있고
아래 사진에서는 age_2는 정의되지 않다고 되어있습니다.
이것으로 보아하니 실제로 var, let, const 모두 Hoisting 되지만 let, const의 경우 접근이 불가능함을 알 수 있습니다.
또한 var와 따라 선언과 동시에 undefined로 자동으로 초기화해주지 않는다는 것도 확인하였습니다.
이렇듯 let, const가 Hoisting 되어 먼저 선언되고 초기화되기 전까지 접근할 수 있는 시기를 TDZ(Temporal Dead Zone)이라고 합니다. 이름 그대로 임시적으로 위험한 순간이라는 느낌입니다.
다만 저는 코드를 작성하면서 TDZ를 신경 쓴 적은 아직 없습니다. ㅎㅎ
마무리하도록 하겠습니다.
var의 불편함(Scope가 이상함, 상수처리 안됨, Hoisting 이슈)을 2015년에 나온 ECMA6에서 새로운 키워드 let, const를 도입함으로써 해소했다.
사용하는 팁은
- const를 기본으로 사용한다. 하지만 변경이 될 수 있는 변수는 let를 사용한다
- var는 절대 사용하지 않는다.
Keyword(키워드) | Scope(영역) | constant(불변), 재할당가능여부 |
var | function | X |
let | block | X |
const | block | O |
'개발 > 자바스크립트' 카테고리의 다른 글
[자바스크립트] JS다운 코드 스타일 #1. 템플릿 리터럴 (0) | 2020.10.07 |
---|---|
[자바스크립트] 배열 비교하는 3가지 방법 + 형님의 솔루션 (0) | 2020.09.27 |
[자바스크립트] for in, for of, 그리고 배열과의 관계에 대해서 (0) | 2020.09.23 |
[자바스크립트] 문자열 파싱(split, ...) 그리고 Set에 대해 (0) | 2020.08.21 |
[자바스크립트] 최적화 #3 문자열 최적화 (+병합, 배열병합) (0) | 2020.08.12 |
댓글