Closure클로저란?
클로저란 함수가 속한 렉시컬 스코프를 기억하여 함수가 렉시컬 스코프 밖에서 실행될 떄도 그 스코프에 접근할 수 있게 하는 기능을 말한다.
function outer() {
var a = 2;
function inner () {
//현재 a를 참조하고 있음
console.log('inner함수실행! a는? ', a); //함수가 속한 렉시컬 스코프 기억 (outer에서 a를 찾아서 기억)
} //결국 inner가 클로져임
return inner;
}
var test = outer(); // 여기서 콘솔을 찍어보면 function inner()가 담겨있음
test(); //inner()가 실행되면서 기억된 a였던 2가 찍힘
inner함수실행! a는? 2
여기서 GC(Garbage Collector)가 outer()의 참조를 없앨거같지만, 내부함수인 inner()가 해당 스코프의 변수인 a를 참조하고 있기 때문에 없애지 않는다.
따라서 스코프 외부에서 inner가 실행되어도 해당 스코프를 기억하기 때문에 2를 출력하게 된다.
즉, **여기서 클로저는 inner()**가 되며 func에 담겨서 렉시컬 스코프도 기억하고 밖에서도 실행된다.
‘반복문 클로저’ 1 2 3 4 찍어보기
function func() {
for (var i = 1; i < 5; i++) {
setTimeout(function () { // 1번
// 3번
console.log('setTimeout', i); // 4번
}, i * 500);
console.log(i, 'i'); // 2번
}
}
func();
- setTimout()이므로 task queue에 쌓이고 기다림
- 그사이 반복문 먼저 돌음
- 이벤트 루프가 감시하고 있다가 콜스택이 빌때 콜백함수를 꺼내와 실행
- 이때 i를 찾으려고 상위 스코프로 가서 i를 찾음. 하지만 이미 5까지 증가했기 때문에 5가 담김
- 이때 클로저는 setTimeout이 됨
반복문 클로저 해결하기
1. 새로운 함수 스코프로 해결하기 (function () {})
function solution1() {
for (var i = 1; i < 5; i++) {
(
function(j) {
setTimeout(function () {
console.log('setTimeout', j);
}, j * 500);
}
)(i);
}
}
solution1(); // 1 2 3 4
setTimeout() 을 IIFE(즉시실행함수 표현식)으로 감싸게 되면, 새로운 스코프를 형성하여 나중에 콜백함수가 j를 참조할때에 그 시점의 i값을 가지기 때문에 1 2 3 4 가 찍히게 된다.
2. 블록 스코프로 해결하기
function solution2() {
for (let i = 1; i < 5; i++) {
setTimeout(function () {
console.log('setTimeout', i);
}, i * 500);
}
}
solution2(); // 1 2 3 4
let은 블록 스코프이기 때문에 블록을 벗어나지 못한다. 따라서 매 반복마다 새로운 let으로 선언된 i이기 때문에 반복이 끝난 이후의 값으로 초기화가 된다.
즉, setTimeout()의 클로저인 콜백함수가 i를 참조하기 위해 상위 스코프를 검색할 때마다 새로 초기화된 i를 참조하기 때문에 1 2 3 4 가 찍히는 것이다.
반응형
'프론트엔드' 카테고리의 다른 글
Prototype 프로토타입 (0) | 2022.11.04 |
---|---|
new 연산자 (0) | 2022.11.04 |
Hoisting 호이스팅 (0) | 2022.11.02 |
Scope 스코프 (0) | 2022.11.02 |
Event Loop 이벤트 루프 (태스크큐 & 마이크로 태스크 큐) (0) | 2022.11.02 |