[JavaScript] var / let / const 차이점 비교 (스코프, 호이스팅, 선언)
- var -
- 선언 했던 변수명 또 해도 된다.
var a = 1;
console.log(a);
var a = 3;
console.log(a);
- 위에서 a는 그냥 출력 된다..;
- scope는 함수 단위다.
함수 밖에서 선언한 변수는 다 global 변수 취급한다.
global 자체도 하나의 함수 단위처럼 작동한다.
반면에 block scope 는 없다.
for/if/while 등 블록에는 scope가 적용되지 않는다.
블록에 있는 변수도 그냥 호이스팅이 적용된다.
function multi() {
console.log(i); //undefined
console.log(a); //undefined
var a = 2;
for(var i = 0; i < 5; i++){
console.log(a * i);
}
console.log(i); //5
console.log(a); //2
}
multi();
i는 다른 언어 같으면 블록 단위로 사용되고 사라져야 한다.
자바스크립트에서는 함수 단위 스코프를 적용한다.
따라서 함수 multi 안에서 호이스팅되고, for문이 끝나고도 살아 있는다.
i=4까지 for문 돌고, 마지막에 for문에서 증감문 i++해서 1=5 되고, for문의 조건문 i<5 에서 걸려서 for문 탈출하고,
이 흔적 그대로 i = 5가 돼서 마지막에 그냥 5가 출력 된다.
변수 a도 이상하다.
위쪽에 등장하는 console.log(a); 는
a를 선언하기도 전에 호출하고 있는데도
에러가 나지 않고 undefined 가 출력되는 정도로 끝난다.
그 이유는 a가 선언되는 순간 함수 multi 안에서 호이스팅되므로,
이미 a가 존재하는 상태로 코드를 읽기 시작하기 때문이다.
아래는 multi()의 실행 결과다.
undefined
undefined
0
2
4
6
8
5
2
- let -
- 같은 이름으로 선언은 한 번만 가능하다.
- scope는 블록 단위다.
- 이게 호이스팅은 var랑 똑같이 되기는 한다.
호이스팅은 똑같이 되는데 어떻게 블록 단위 스코프를 적용하는가?
런타임에서 값을 정의(초기화)하기 전까지는 접근만 막는 식으로 작동한다.
이러한 기능을 구현하기 위해서 새로운 개념을 만든다.
TDZ - Temperaly Dead Zone 라는 개념이다.
일시적으로 죽어 있는 구역이라는 뜻이다.
이 구역에 들어 있는 변수들에는 값을 정의하기 전까지는 접근할 수 없다.
let 변수들은 호이스팅이 되기는 하지만 TDZ에 넣어둔다.
그러다가 정의되고 나면 접근 가능하도록 TDZ로부터 꺼내 준다.
function multi() {
console.log(i); //Uncaught ReferenceError: i is not defined at multi()
console.log(a); //Uncaught ReferenceError: Cannot access 'a' before initialization at multi()...
let a = 2;
for(let i = 0; i < 5; i++){
console.log(a * i);
}
console.log(i); //Uncaught ReferenceError: i is not defined at multi()
console.log(a); //2
}
multi();
아까와 똑같은 코드다.
다른 점이 있는데, for문에서 i를 var가 아니라 let로 선언했다.
a도 마찬가지로 let로 선언했다.
i를 var로 선언했을 때는 별 문제 없이 작동했다.
unidentified가 출력될 뿐이었다.
i를 let로 선언했을 때는 작동하지 않는다.
에러가 발생하기 때문이다.
i를 let로 선언했기 때문에 블록 스코프가 적용되고,
for문 안에서 쓰이고 사라진다.
밑에서 i를 호출해 봤지만, 그런 변수는 못 찾겠다며
에러를 일으키고 작동을 멈춰 버린다.
에러 내용은 다음과 같다.
Uncaught ReferenceError: i is not defined at multi() ....
a도 비슷하다.
a를 선언할 때 함수 multi 안에서 호이스팅되므로 찾을 수는 있지만,
값을 넣어주기 전까지는 접근할 수 없다는 에러를 낸다.
let a = 2; 를 실행하기 전까지는 console.log(a); 가 에러를 내지만,
아래쪽 console.log(a); 는 정상적으로 a가 출력된다.
- const -
- 다른 언어들에서도 쓰는, 상수 같은 개념이다.
- 같은 이름으로 두 번 이상 선언할 수 없다.
- 한 번 값을 정의하면 변경할 수 없다.
- scope는 블록 단위다.
function multi() {
console.log(a); //ReferenceError: Cannot access 'a' before initialization at multi()...
const a = 2;
console.log(a); //2
for(let i = 0; i < 5; i++){
console.log(a * i);
}
a = 5; //Uncaught TypeError: Assignment to constant variable. at multi()....
}
가장 위쪽에 있는 console.log(a);에서는 에러가 나면서 작동을 멈춘다.
a를 const로 선언했기 때문이다.
let으로 선언했을 때와 마찬가지로,
선언되는 순간 함수 단위로 호이스팅이 적용되기는 하지만,
그 변수 안에 값을 넣어주기 전까지는 접근할 수 없다.
Cannot access 'a' 어쩌구, 'a'에 접근할 수 없다고 한다.
아래쪽에 있는 a = 5; 에서도 에러와 함께 작동을 멈춘다.
const로 선언한 변수 a에 다른 값을 넣어달라고 했기 때문이다.
const는 값을 한 번 넣으면 바꿀 수 없다.
Assignment to constant variable 상수 변수에 값을 할당 어쩌구....
- 함수의 호이스팅 -
/함수 선언문
- function add(x, y) { return x + y;}
이거는 호이스팅될 때 정의도 같이 된다.
내용이 채워진 채로 호이스팅된다는 말이다.
그래서 바로 실행 가능하다.
console.log(add()); //output: f add(x, y)
/함수 표현식
- var sub = function(x, y) { return x - y; }
이거는 호이스팅될 때 그냥 변수처럼 취급한다.
그래서 변수 껍데기만 간다.
선언은 한 상태지만 정의가 되지 않은 상태다.
console.log(sub) // output: undefined
/ES5까지는 var만 있었다.
var 의 문제가 이런저런 많았다.
이러한 문제들을 해결하기 위한 방법을 고민했다.
그 결과 ES6로 업그레이드하면서(2015년 즈음) let/const 개념을 만들었다.
그래서 let과 const는 약간 동기 같은 느낌이다.
만들면서 scope 단위도 똑같이 만든 듯하다.
기본적으로 변수의 스코프는 최대한 좁게 만드는 것을 권장한다.
따라서 var 보다는 let을 쓰고, let 중에서도 변경할 수 없게 만들려면 const를 쓴다.
참고 자료 : https://www.youtube.com/watch?v=fETYLCU2YYc