클로저
클로저를 쉽게 정의하면 다음과 같이 정의할 수 있다.
클로저란 자신이 생성될 때의 환경을 기억하는 함수이다.
클로저는 자바스크립트 고유의 개념이 아니라 다양한 프로그래밍 언어에서 사용되는 중요한 특성이다. 위 정의에서 말하는 '함수'란 반환된 내부함수를 의미하고 '자신이 생성될 때의 환경'이란 내부함수가 선언됬을 때의 스코프를 의미한다.
즉, 클로저는 반환된 내부함수가 자신이 선언됬을 때의 스코프를 기억하여 해당 스코프 외부에서 호출되어도 해당 스코프에 접근할 수 있는 함수를 말한다.
function outer() {
var a = 2;
function inner() {
console.log(a);
}
return inner;
}
var func = outer();
func(); // 2
위의 코드에서 outer 함수는 함수를 반환하고, 반환된 함수는 outer 함수 내부에서 선언된 변수를 참조하고 있다. 또한 이렇게 참조된 변수는 함수 실행이 끝났다고 해서 사라지지 않았고, 여전히 제대로 된 값을 반환하고 있는 걸 알 수 있다. 여기서 클로저는 inner 함수가 되며 func에 담겨 밖에서도 실행되고, 렉시컬 스코프를 기억한다.
클로저의 활용
전역변수 대체
// Counter 예제
const btn = document.querySelector('button')
btn.addEventListener('click',handleClick)
let count = 0
function handleCilck(){
count++
return count
}
위 코드에서 count라는 전역변수를 사용해서 count 증가 기능을 구현하고 있다. 이를 클로저로 구현하면 다음과 같다.
// Counter Closure 예제
const btn = document.querySelector('button')
btn.addEventListener('click',handleClick())
function handleCilck(){
let count = 0
return function (){
count++
return count
}
}
// 위와 같이 작성해 준다면 외부함수(handleClick)의 lexical environment를 참조하는 함수를
// btn의 콜백함수로 이용해 전역객체 없이 구현할 수 있다.
이외에도 자바스크립트를 객체지향으로 사용할 때 은닉화 등에 클로저가 사용된다.
※ 반복문 클로저
function func() {
for (var i=1; i<5; i++) {
setTimeout(function() { console.log(i); }, i*500);
}
}
func(); // 5 5 5 5
위 코드는 간단하게 1부터 4까지의 정수를 출력하는 코드이지만 실제로 돌려보면 5가 4번 출력되는 엉뚱한 결과가 나온다. 그 이유는 setTimeout()을 반복문 안에서 돌리면 콜백함수가 계속해서 태스크 큐에 쌓이게 되고 반복문이 다 돌고나서야 콜 스택으로 돌아와서 실행된다. 콜백함수는 클로저이기 때문에 상위 스코프에서 i의 값을 물어보고 상위 스코프인 func의 스코프에선 i가 5까지 증가했기 때문에 5가 4번 출력되는 것이다.
해결 방법
1. 새로운 함수 스코프로 해결하기
function func() {
for (var i = 1; i < 5; i++) {
((j) => {
setTimeout(() => { console.log(j); }, j * 500);
})(i);
}
}
func(); // 1 2 3 4
setTimeout()을 IIFE(즉시실행함수 표현식)로 감싸게 되면, 새로운 함수 스코프를 생성하고 나중에 콜백함수가 j를 참조할 때 해당 시점의 i 값을 갖기 때문에 원하는 결과를 얻을 수 있다.
2. 블록 스코프로 해결하기
function func() {
for (let i=1; i<5; i++) {
setTimeout(function() { console.log(i); }, i*500);
}
}
func(); // 1 2 3 4
함수 스코프가 아닌 블록 스코프를 갖는 let을 사용하면 for문 내의 스코프를 갖기 때문에 새로운 i가 선언되고 반복이 끝난 이후의 값으로 초기화된다. 따라서 setTimeout()의 클로저인 콜백함수가 i를 참조하기 위해 상위 스코프를 검색할 때 블록 스코프에서 매 반복마다 선언 및 초기화된 i를 참조하기 때문에 원하는 결과를 얻을 수 있다.
'Web-Frontend > JavaScript' 카테고리의 다른 글
[JS] 자바스크립트의 클래스 (0) | 2023.10.19 |
---|---|
[JS] 함수 호출 방식과 this 바인딩 (0) | 2023.10.03 |
[JS] 이벤트의 전파와 흐름 (이벤트 버블링, 캡처링, 위임) (0) | 2023.10.02 |
[JS] 자바스크립트 배열 메소드 정리 (0) | 2023.10.02 |
[JS] 자바스크립트의 여러가지 배열 생성 방법 (0) | 2023.10.02 |