4-클로저
코드 블록의 Try it Yourself 버튼으로 직접 실행할 수 있다.
구문
목차
1. 가비지컬렉션
콜스택에서 메모리를 자동으로 삭제하는 과정을 가비지 컬렉션이라고 하고 이 시스템을 가비지 컬렉션 이라고 한다. 메모리의 누수를 방지하려는 목적으로 평가가 완료된 코드를 콜스택에서 제거하는 것인데 자바스크립트는 이것을 자동으로 지원한다.
1-1-**가비지 컬렉터(Garbage Collactor): **
메모리에서 더이상 사용되지 않는 실행컨텍스트 감지하고 해당 메모리를 회수 하여 삭제하는 시스템
function outer() {
let a = 10;
return function inner() {
a++;
console.log(b);
}
}
const fn = outer();
fn();
위 코드의 결과를 예상해 보자

11이라는 값이 평가되고 가비지컬렉터로 컨텍스트가 이동하면서 콜스택은 비워져 있을것이다.
function outer() {
let a = 10;
return function inner() {
a++;
console.log(a);
}
}
const fn = outer();
fn();
fn();
fn();
하지만 호출할때 마다 값이 증가하는 이상한 일이 발생한다.
1-2-예외
컨텍스트의 레코드 값이 참조중 일 때는 가비지컬렉터가 자동으로 수행되지 않는다.

inner 의 a 가 상위컨텍스트인 a의 값을 참조하므로 outer 는 가비지컬렉터 대상이 되지 않는다.
2-클로저
클로저는 1-2에서 발생하는 현상을 말한다.
2-1-정의
Info: 함수가 생성될 때 그 주변의 변수 환경(스코프)을 기억하고, 생성 이후에도 접근할 수 있는 기능
가비지컬렉션의 대상이 되지 못하고 메모리 제거가 되지 못해 메모리에 남아 있는 현상
2-2-개념요약
- 함수가 만들어질 때의 변수 환경을 기억 → 함수가 실행될 때만 아니라, 이후에도 해당 변수에 접근 가능
- 내부 함수가 외부 함수의 변수를 참조 → 내부 함수가 실행되는 동안 외부 함수의 변수는 유지됨
- 데이터 은닉(캡슐화) → 클로저를 활용하면 외부에서 직접 접근할 수 없는 변수를 만들 수 있음
2-3-활용
- 변수의 유지 (상태 유지 기능) → 한 번 실행된 함수의 값을 저장하고 다시 사용할 때
- 데이터 보호 (정보 은닉) → 외부에서 직접 변경할 수 없도록 변수를 보호할 때
- 콜백 함수 활용 → 비동기 처리나 이벤트 핸들러에서 이전 상태를 유지할 때
2-4-예시
정보를 안전하게 은닉(Information Hiding)
let n = 0;
const increase = function () {
return ++n;
}
console.log(increase());
console.log(increase());
console.log(increase());
위의 코드는 잠재적으로 버그가 있다. 변수 n은 increase 함수의 호출시에만 변경되어야 전역변수라 다른 코드에 의해값을 변경할수 있기 때문이다.
const increase = function () {
let n = 0;
return ++n;
}
console.log(increase());
console.log(increase());
console.log(increase());
위와 같이 수정하면 n은 지역변수로 increase 함수만 변경할수 있게된다. 누산을 하지 못하는 것이다.
2-4-1-클로저패턴
const increase = (function () {
let n = 0;
return function () { return ++n }
}())
console.log(increase());
console.log(increase());
console.log(increase());
위와 같이 수정해보자 increase 변수의 함수를 즉시실행함수로 수정한다.
2-4-2-소스코드의 평가단계
1단계: 전역 실행 컨텍스트 생성
- 코드 실행이 시작되면, 자바스크립트 엔진은 전역 실행 컨텍스트(Global Execution Context) 를 생성한다.
- 전역 컨텍스트에는
increase변수와 즉시 실행 함수(IIFE)가 포함된다.
2단계: 즉시 실행 함수 (IIFE) 실행
- 즉시 실행 함수
(function() \{ ... \}())가 실행되면서 새로운 실행 컨텍스트 가 생성된다. n이라는 렉시컬 환경(Lexical Environment) 내부 변수가 선언되고, 초기값0이 할당된다.- 이 함수는 실행된 후 내부에서 또 다른 익명 함수를 반환하고 종료된다.
3단계: 반환된 함수가 increase에 할당됨
- 즉시 실행 함수가 종료될 때
return function () \{ return ++n \}문으로 인해 내부 함수가 반환된다. - 즉,
increase변수에는 반환된 내부 함수의 참조(클로저) 가 저장된다.
4단계: increase() 호출 과정 (클로저 활용)
이제 console.log(increase()) 를 실행할 때마다 함수 실행 컨텍스트가 어떻게 변하는지 보자.
**첫 번째 호출: **console.log(increase());
increase()실행 → 내부 함수가 실행됨.increase내부의n값을 증가 (++n → 1)하고 반환.console.log(1)출력.- 중요한 점: 내부 함수 실행이 끝나도
n값(=1)은 유지됨.
**두 번째 호출: **console.log(increase());
increase()실행 → 내부 함수가 실행됨.- 기존의
n값(1)에서++n → 2로 증가. console.log(2)출력.n** 값(=2)이 여전히 유지됨.**
**세 번째 호출: **console.log(increase());
increase()실행 → 내부 함수가 실행됨.- 기존의
n값(2)에서++n → 3으로 증가. console.log(3)출력.n** 값(=3)이 계속 유지됨.**
이렇게 클로저를 활용하면 함수내부에서 사용하는 데이터를 은닉하여 안정성이 보장된 코드를 구현할 수 있다.
2-5-클로저 활용시 유의사항
클로저는 결국 가비지 컬렉션이 되지 않는 코드를 의미한다. 클로저가 발생한 데이터의 참조가 끝나더라도 메모리에서 자동삭제가 되지 않으므로 수동삭제 하는 로직을 작성해야 한다.
2-5-1-클로저의 삭제1
2-4-1의 예시처럼 즉시실행함수를 사용하면 함수의 실행후 콜스택에서 삭제 되므로 메모리 누수를 방지할수 있다.
2-5-2-클로저의 삭제2
아래와 같이 선언적 함수를 식별자에 할당하여 호출하는 경우
function counter() {
let a = 0;
return function inner() {
++a;
console.log(a);
}
}
let fn = counter();
**fn();
fn();
fn();
fn();
fn();
fn=null**
counter 함수의 호출이 끝나면 fn 에 null을 할당하여 메모리를 비워야 한다.