언어/자바스크립트

[Javascript] 클로저

youngble 2022. 7. 3. 00:44

해당 함수 실행컨텍스트가 종료된 이후에도 소멸하지않고 호출하여 내부함수가 외부 함수의 변수를 참조 접근하여 사용할수 있게 하는 특성을 갖는 현상/함수

  • 해당함수에서 함수자체를 참조함으로써 outer인 외부함수의 실행 컨텍스트가 종료되는 시점에서 가비지 컬렉터의 수집대상에서 제외되어 해당 LexicalEnvironment의 정보를 사용할수 있게 한다.
  • 어떤 함수 A에서 선언한 변수 a를 참조하는 내부함수 B를 외부로 전달할 경우 A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상

예시 코드를 보면서 비교하면 더욱 이해하기 쉽다.

1) 기존 일반함수, 내부함수 사용

위와같이 inner함수를 outer함수에서 return 해주고 outer2에 담아 console.log에 각각 찍어보면 

 inner의 ++a하기위해 inner안(environmentRecord)에서 a라는 변수를 찾고 없기때문에 outerEnvironmentReference에 지정된 outer의 LexicalEnvironment에 접근하여 a변수를 찾는다. 이때 a는 1이기때문에 ++a를 하여 2가 콘솔로그에 출력된다.

그다음 outer가 실행컨텍스트가 종료되면서 콜스택에서 사라지므로 2를 담은 a라는 정보도 가비지컬렉터에 넘어가면서 소멸된다.

따라서 두번째 콘솔로그에서 outer2를 하면 다시 2가 출력되는걸 확인할 수 있다.

2) 클로저 

클로저 함수는 외부 함수의 실행이 끝나더라도 외부 함수 내 변수를 사용할 수 있다. 클로저는 이처럼 특정 데이터를 스코프 안에 가두어 둔 채로 계속 사용할 수 있게하는 폐쇄성을 갖는다.

클로저를 사용하면 콘솔로그에 처음에 2를 출력하고 두번째 콘솔로그에서 3을 출력하는데 기존의 a의 값이 가비지컬렉터 대상이 되지않고 미래에 사용가능하기 때문에 소멸되지않은 상태로 만들기 때문이다. 원리는 부함수 inner를 실행결과 inner()로 return하지않고 inner함수 자체를 return 해줌으로써 outer2는 inner함수 자체를 참조하게 되는 것이다. 이는 가비지컬렉터의 동작방식때문에 생기는 것인데 가비지컬렉터는 어떤값을 참조하는 변수가 하나라도 있다면 수집대상에서 제외되기 때문이다. 따라서 변수 outer2는 inner를 참조하기때문에 inner함수의 외부함수의 LexicalEnvironment가비지 컬렉팅 되지않기 때문에 a는 소멸되지않는 것이다. 아래는 콜스택실행컨텍스트를 그림으로 좀더 이해하기 쉽게 표현되어있다.

흥미로운 점은 위의 outer의 environmentRecord에서 a를 제외하고 나머지는 제거되는데 이는 2019년 기준으로 크롬이나 nodejs등에서 사용하는 V8엔진 기준으론 내부함수에서 실제로 사용하는 변수를 제외하고 나머지는 GC(Garbage Collecting, 가비지콜렉팅)하도록 최적화되어 있다고한다.