LeetCode 문제를 풀다가 도저히 안풀려서 다른 사람들의 풀이를 봤는데 2중 for문에서 바깥의 for문에는 let
을 사용하고, 안쪽 for문에는 var
를 사용하는 것을 보고 '왜.. 이렇게 쓰지??' 라는 생각을 했다.
이전 포스트에서 var, let, const의 차이를 다루긴 했었지만 그때 다뤘던 내용에는 없는 이유로 사용하는 것 같아서 한번 정리하면 좋겠다! 라고 생각해서 이렇게 포스트로 따로 정리하게 되었다.
이전 포스트에선 var과 let의 차이점은 변수 재선언 가능과 불가능이라고 정리했었는데, 사실을 또 다른 차이점이 있었고 그게 바로 Scope다.
⚡ Scope : 식별자(변수, 함수, 클래스) 접근 규칙에 따른 유효 범위
Scope는 Block(중괄호) 또는 Function에 의해서 구분된다는 특징을 가지고 있으며 각각 Block Scope, Function Scope라고 불린다.
❗ 하지만 Arrow Function은 Block Scope로 취급된다 ❗
또한 가장 바깥쪽에 존재하는 Scope를 Global Scope(전역 스코프)라고 부르며 그 이외의 다른 스코프들은 전부 Local Scope(지역 스코프)이다.
위에서 언급했듯이 Scope는 Block Scope, Function Scope 둘로 구분된다고 했는데,
var는 Function Scope, let은 Block Scope를 가진다는 차이점이 있다.
따라서 var
는 for문에서 따로 scope를 가지지 않기 때문에 (Function Scope이므로)
기존에 선언되어 있던 전역변수 값을 바꿔버리는 상황이 발생할 수도 있다!
var i = 100; // 전역 변수
for(var i = 0; i<4; i++){
console.log(i); // 0 1 2 3 출력
}
console.log(i); // 4 출력 (전역변수의 값이 바뀌지 않았다면 100 출력)
let i = 100; // 전역 변수
for(let i = 0; i<4; i++){ // let i <- Block Scope
console.log(i); // 0 1 2 3 출력
}
console.log(i); // 100 출력 (전역변수의 값이 바뀌지 않아 그대로 출력)
for문과 setTimeout을 함께 사용하는 코드에서도 var을 사용시에는 예상치 못한 문제와 맞딱뜨리게 된다.
예시 코드를 보면서 이해해보자!
for(var i=0; i<4; i++){
setTimeout(() => {
console.log(i);
}, 1000 * (i+1));
}
원래의 의도라면 1초마다 0 1 2 3 이 출력 되어야 하지만! 위 코드를 실행하면 4가 4번 출력되는 것을 확인 할 수 있다 🤔
for(var i=0; i<4; i++){
console.log(`for문 ${i}`);
setTimeout(() => {
console.log(`setTimeout ${i}`);
}, 1000 * (i+1));
}
위의 코드처럼 console.log
를 찍어서 확인해보면 아래와 같은 결과가 나타나게 된다.
결과를 자세히 보면 for문을 다 돌고 나서야 setTimeout의 콜백함수를 수행하는 것을 알 수 있다.
이 문제는 Scope 문제가 아닌 비동기 관련 문제이다! for문은 엄청나게 빠른 속도로 수행되기 때문에 setTimeout은 이미 증가된 i를 만날 수 밖에 없는 것이다.
for(let i=0; i<4; i++){
setTimeout(() => {
console.log(i);
}, 1000 * (i+1));
}
let
으로 바꾼 위 코드를 실행시키면 원했던 대로 0 1 2 3 이 출력되는 것을 확인 할 수 있다.
(for문을 다 돌고 setTimeout이 실행되는 비동기 문제는 그대로다! 이 문제가 해결 된 것이 아님!)
이런 결과를 확인 할 수 있는 이유는, Block Scope를 가지는 let
을 이용해서 변수를 선언 했기 때문에 for문을 한번 돌 때마다 i=0인 scope
, i=1인 scope
, i=2인 scope
, i=3인 scope
를 생성하게 된다.
비동기 문제로 for문은 먼저 실행되긴 하지만 setTimeout이 실행 될 때 위에서 생성된 4개의 scope에서 각각 i값을 찾기 때문에 원했던 결과가 출력 되게 되는 것이다.
잘 정리하신 내용 잘 봤습니다~ ㅎㅎ