[JavaScript] let & var & 클로저

HYl·2022년 3월 13일
0

먼저, let 으로 반복문을 작성해보자.

for (let i = 0; i < 6; i++) {
 setTimeout(() => {
 	console.log('i', i); 
 }, 1000 * (i + 1));
}

결과

i 0
i 1
i 2
i 3
i 4
i 5

  • 위의 코드를 실행 했을 때, i 값이 0 ~ 5 로 결과가 나온다.

var 로 반복문을 작성해보자.

for (var i = 0; i < 6; i++) {
 setTimeout(() => {
 	console.log('i', i); 
 }, 1000 * (i + 1));
}

결과

i 6

  • 위의 코드를 실행했을 때, 모든 i가 6으로 나온다.
    이것이 var와 let의 결정적인 차이이다.
    어째서 이렇게 다른 결과가 나오는 것일까 ?

변수는 스코프 ( scope, 범위 ) 라는 것을 가진다.

  • var : 함수 스코프
  • let, const : 블록 스코프

스코프라는 것이 무슨 소리일까 ? 예제를 보면서 알아보자.

function b () {
	var a = 1;
}

console.log(a); // Uncaught ReferenceError: a is not defined

a를 콘솔로 출력하면 에러가 발생한다.
함수를 경계로 접근 여부가 달라지는 것을 함수 스코프 라고 한다.

if (true) {
	var a = 1;
}

console.log(a); // 1

var는 함수 스코프라서, 함수만 신경쓰기 때문에 if문 안에 들어 있으면 바깥에서 접근 가능하다.

if (true) {
	let a = 1;
}
 
console.log(a); //  Uncaught ReferenceError: a is not defined

let은 에러가 발생한다.
why ? let은 블록 스코프이기 때문이다.


처음에 언급했었던 반복문에서의 let과 var를 사용한 결과가 다른 이유를 더 자세히 살펴보자.

var 를 사용한 반복문

for (var i = 0; i < 6; i++) {
 setTimeout(() => {
 	console.log('i', i); 
 }, 1000 * (i + 1));
}

setTimeout의 콜백 함수 안에 있는 i 와 바깥의 1000 * (i + 1)의 i는 다른 시점에 실행된다

  • 1000 * (i + 1) : 반복문이 돌 때 실행된다.
  • setTimeout의 콜백 함수는 지정한 시간 뒤에 호출된다.

but, 반복문은 매우 빠른 속도로 돌아서 콜백 함수가 실행될 때는 이미 i가 6이 되어 있는 것이다.

실행순서

i가 0일 때 setTimeout(콜백0, 1000) 실행
i가 1일 때 setTimeout(콜백1, 1000) 실행
i가 2일 때 setTimeout(콜백2, 1000) 실행
i가 3일 때 setTimeout(콜백3, 1000) 실행
i가 4일 때 setTimeout(콜백4, 1000) 실행
i가 5일 때 setTimeout(콜백5, 1000) 실행
i가 6이 됨
1초 후 콜백0 실행 (i는 6)
2초 후 콜백1 실행 (i는 6)
3초 후 콜백2 실행 (i는 6)
4초 후 콜백3 실행 (i는 6)
5초 후 콜백4 실행 (i는 6)
6초 후 콜백5 실행 (i는 6)

  • 따라서, 콜백 함수가 실행될 때 i를 콘솔로 출력하면 6이 나오게 된다.

그렇다면 왜 let을 쓸 때는 이런 문제가 발생되지 않을까?


let 을 사용한 반복문

for (let i = 0; i < 6; i++) {
 setTimeout(() => {
 	console.log('i', i); 
 }, 1000 * (i + 1));
}
  • for문으로 쓰이는 let은 하나의 블록마다 i가 고정된다. (블록 스코프의 특성)
  • 따라서, setTimeout 콜백 함수 내부의 i도 setTimeout을 호출할 때의 i와 같은 값이 들어간다.

반복문과 var를 쓸 때, 항상 스코프 관련 문제가 생기는 것은 아니다.
setTimeout 같은 비동기 함수와 반복문, var를 만났을 때 이런 문제가 발생한다.


var와 비동기가 만나면 클로저 문제가 발생한다. ( 함수와 함수 밖의 변수 관계 )

  • 해결 방법은 ? 함수 안의 변수 관계로 변경한다.

for (var i = 0; i < 6; i++) {
  (function(j) {
    setTimeout(() => {
      console.log('j', j); 
    }, 1000 * (i + 1));
  })(i);
}

결과

j 0
j 1
j 2
j 3
j 4
j 5

  • 위의 코드를 실행 했을 때, j 값이 0 ~ 5 로 결과가 나온다.

REFERENCE

profile
꾸준히 새로운 것을 알아가는 것을 좋아합니다.

0개의 댓글