우리는 변수를 선할 때 var
, let
, const
키워드를 사용한다. ES6에서 let, const가 도입되기 전에는 var가 오랬동안 유일하게 변수 선을 위해 사용되어 왔다. 하지만 ES6이후에 대부분의 코드에서 var를 사용하지 않고 있다. 오늘은 왜 var를 꺼려하는지에 대해서 알아보겠다.
자바스크립트에서 호이스팅은 꽤나 중요한 개념이다. 호이스팅은 단순 번역으로 끌어올린다라는 의미다. 자바스크립트에선 인터프리터가 변수와 함수의 메모리 공간을 선언하기 전에 미리 할당하는 것을 의미한다. 그렇기 때문에 var, let, const 모두 호이스팅이 일어난다. 하지만 var의 경우 호이스팅 후 변수를 undefined로 값이 초기화되기 때문에 선언 전에 참조가 가능해진다.
console.log(a); // ReferenceError: Cannot access 'i' before initialization
let a;
console.log(b); // undefined
var b;
한마디로 변수의 선언 위치가 자유로워지고 이 변수를 내가 undefined로 할당을 한건지 아니면 원하는 값으로의 할당이 아직 이루어지지 않았는지 확인이 힘들다.
let과 const의 경우 선언한 변수를 다시 선언하는 경우 에러를 발생한다. 하지만 var의 경우 변수 이름이 중복되어도 에러가 발생하지 않는다.
let a;
let a; // SyntaxError
var b;
var b; // No problem~!!
이 특성 때문에 원치않게 이미 선언한 변수의 값에 영향을 미치게 되어도 실수를 알아차리기 힘들다.
let, const의 경우는 블록 레벨 스코프를 갖고, var의 경우 함수 레벨 스코프를 갖게 된다. 이 부분은 예제를 먼저 보겠다.
for (let a = 0; a < 3; a++) {
...
}
console.log(a); // ReferenceError
for (var b = 0; a < 3; b++) {
...
}
console.log(b); // 3
예제처럼 let의 경우 for문 안의 블록 밖에서는 변수가 참조가 불가능하지만 var의 경우 변수가 함수 레벨 스코프이기에 블록 밖에서도 참조가 가능하다.
위의 예제에서는 동작에 문제가 없지만 setTimeout() 함수와 함께 비동기로 사용한다면 문제가 발생한다.
for (let a = 1; a < 4; a++) {
setTimeout(() => {
console.log(a + "번째");
}, 1000);
}
// 1 번째
// 2 번째
// 3 번째
for (var b = 1; b < 4; b++) {
setTimeout(() => {
console.log(b + "번째");
}, 1000);
}
// 4 번째
// 4 번째
// 4 번째
for문이 실행되면서 a 또는 b 가 0인 스코프, 1인 스코프, 2인 스코프 ... 들을 생성해줍니다. let을 사용한 경우는 for문의 범위가 적용되기 때문에 이미 for문이 다 실행되었어도 이전의 스코프의 a 값을 참조하지만 var의 경우 함수 레벨 스코프이므로 for문이 실행되면서 생성한 스코프를 빠져나와서 최좀의 b를 참조하게 되어 예상한 결과와 다르게 나온다.
var의 경우 호이스팅 후 undefined로 초기화가 되어 해당 undefined가 내가 할당했는지 자동으로 초기화된 값인지 확인이 어렵고 var로 동일한 변수 이름을 중복하여 선언이 가능하므로 var로 변수 선언 시 이전의 변수 값에 영향을 미치는 건지 모호해진다. 그리고 블록 레벨 스코프가 아닌 함수 레벨 스코프이기 때문에 필요하지 않은 전역변수가 많아질 수 있고, 비동기처리시 원하지 않게 동작할 수 있다.
💡직접 읽어보면 뼈가 되고 살이 되는 출처
👉모던 자바스크립트 DEEP DIVE(도서 이웅모 저)
👉https://developer.mozilla.org/ko/docs/Glossary/Hoisting
👉https://velog.io/@yonyas/Javascript-for문과-setTimeout-동시에-쓸-때-var와-let의-차이