JavaSctipt

[Javascript] 클로저 (Closure)

판교너굴맨 2021. 7. 6. 20:19

클로저란

간략하게 말하면 외부 변수를 기억하고, 이 외부 변수에 접근할 수 있는 함수를 클로저라고 한다.
  • 함수와 렉시컬 환경의 조합
  • 함수가 선언 될 당시의 렉시컬 환경을 기억한다. (외부 변수를 기억한다.)
  • 생성 이후에도 계속 접근 가능

 

클로저를 알아보기 전에 먼저 렉시컬 환경(Lexical environment)에 대해 먼저 알아보자

렉시컬 환경이란 (Lexical environment)

함수, 코드 블록, 스크립트 전체가 실행되기 전에 생성되는 스코프 범위 내의 함수와 변수를 프로퍼티로 저장하는 객체이다.

실행중인 함수, 코드 블록, 스크립트 전체는  _Lexical Environment_ 라고 불리는 내부 숨김 연관 객체를 갖는다.

 

_Lexical Environment_ 객체는 두가지로 구성 되어 있다.

  1. 환경 레코드 : 모든 지역 변수를 프로퍼티로 가지고 있는 객체이다. this와 같은 기타 정보도 여기에 포함 된다.
  2. 외부 렉시컬 환경에 대한 참조 : 코드를 실행할 때 필요한 변수를 먼저 환경 레코드에서 찾아보고, 없다면 상위의 외부 렉시컬 환경을 참조하여 찾는다. (렉시컬 스코프)

클로저와 렉시컬 환경

const func = () => {
  let x = 0;
  const innerFunc = () => console.log(x);
  return innerFunc;
}
const inner = func();
inner(); // 0;

위 코드의 순서를 살펴 보자

1. func 함수가 실행 되고, innerFunc함수를 반환한다.

2. 실행된 func 함수는 innerFunc 함수를 반환 하고, 지역 변수인 x와 같이 callStack에서 사라진다. 

3. 하지만 내부 함수인 innerFunc 함수를 외부함수인 func 함수 밖에서 실행 해도  외부 변수인 x 값을 출력할 수 있다.

 

리턴 된 내부 함수 innerFunc는 외부 함수인 func 밖에서 호출 되어도 (생성 되었을 때의 스코프 밖에서 실행 되어도), innerFunc 함수 자신이 생성 되었을 때의 렉시컬 환경을 기억하고 x 변수에 접근할 수 있는 것이다. 

즉, 클로저는 자신이 생성 됐을 때의 렉시컬 환경을 기억하고 있는 함수라고 할 수 있다.

클로저 사용 이유

1. 변수의 현재 상태를 기억하고 최신 상태를 유지

const increase = (() => {
    let count = 0;
    const innerIncrease = () => count += 1;
    return innerIncrease;
})();

increase(); // 1
increase(); // 2
increase(); // 3

위의 코드에서 외부함수인 increase는 내부함수인 innerIncrease를 반환하고 소멸한다. innerIncrease는 자신이 생성됐을 때의 렉시컬 환경을 기억하고 있기 때문에 (스코프 범위내의 함수와 변수를 기억하고 있기 때문에) 렉시컬 환경에서 count 변수에 접근하고, 최신 상태를 유지할 수 있다.

2. 전역 변수 사용 억제

클로저 기능이 없다면 위의  count 변수를 전역 변수로 사용해야 한다.

let count = 0;

const increase = () => count += 1

increase(); // 1
increase(); // 2
increase(); // 3

위의 코드와 같이 변수를 전역적으로 사용하면 누구든 count 변수에 접근할 수 있어서 의도치 않게 count의 값을 변경시킬 수도 있다. 또 전역 변수 값을 변경 시키는 건 순수함수에 위배된다. 

아래와 같이 클로저를 사용하도록 하자.

const increase = (() => {
    let count = 0;
    const innerIncrease = () => count += 1;
    return innerIncrease;
})();

increase(); // 1
increase(); // 2
increase(); // 3

3. 은닉화 캡슐화

위의 코드를 보면 count 변수는 외부에서 직접 접근할 수 없고 increase 함수로만 접근할 수 있다. 이렇게 자바스크립트에는 없는 캡슐화를 구현할 수 있다.