[TIL #25] var과 let의 차이 (feat. for문, setTimeout)

JMinkyoung·2022년 2월 1일
1

TIL

목록 보기
25/42
post-thumbnail

LeetCode 문제를 풀다가 도저히 안풀려서 다른 사람들의 풀이를 봤는데 2중 for문에서 바깥의 for문에는 let을 사용하고, 안쪽 for문에는 var를 사용하는 것을 보고 '왜.. 이렇게 쓰지??' 라는 생각을 했다.

이전 포스트에서 var, let, const의 차이를 다루긴 했었지만 그때 다뤘던 내용에는 없는 이유로 사용하는 것 같아서 한번 정리하면 좋겠다! 라고 생각해서 이렇게 포스트로 따로 정리하게 되었다.

Scope 란?

이전 포스트에선 var과 let의 차이점은 변수 재선언 가능과 불가능이라고 정리했었는데, 사실을 또 다른 차이점이 있었고 그게 바로 Scope다.

Scope : 식별자(변수, 함수, 클래스) 접근 규칙에 따른 유효 범위

Scope는 Block(중괄호) 또는 Function에 의해서 구분된다는 특징을 가지고 있으며 각각 Block Scope, Function Scope라고 불린다.

❗ 하지만 Arrow Function은 Block Scope로 취급된다 ❗

또한 가장 바깥쪽에 존재하는 Scope를 Global Scope(전역 스코프)라고 부르며 그 이외의 다른 스코프들은 전부 Local Scope(지역 스코프)이다.

var과 let의 scope 차이

위에서 언급했듯이 Scope는 Block Scope, Function Scope 둘로 구분된다고 했는데,
varFunction Scope, letBlock Scope를 가진다는 차이점이 있다.

따라서 var는 for문에서 따로 scope를 가지지 않기 때문에 (Function Scope이므로)
기존에 선언되어 있던 전역변수 값을 바꿔버리는 상황이 발생할 수도 있다!

var의 경우

var i = 100;	// 전역 변수

for(var i = 0; i<4; i++){
  console.log(i);	// 0 1 2 3 출력
}

console.log(i);	// 4 출력 (전역변수의 값이 바뀌지 않았다면 100 출력)

let의 경우

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

for문과 setTimeout을 함께 사용하는 코드에서도 var을 사용시에는 예상치 못한 문제와 맞딱뜨리게 된다.
예시 코드를 보면서 이해해보자!

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를 만날 수 밖에 없는 것이다.

let으로 해결

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값을 찾기 때문에 원했던 결과가 출력 되게 되는 것이다.

참고 자료
참고 자료

profile
Frontend Developer

1개의 댓글

comment-user-thumbnail
2022년 4월 21일

잘 정리하신 내용 잘 봤습니다~ ㅎㅎ

답글 달기