Closure

2020. 4. 28. 11:41JavaScript

* Closure

 - 외부함수의 변수들에 접근 가능한 내부함수를 의미

 - 해당 내부함수는 함수 자체로 외부함수에서 리턴되는 형태

 

pass me를 풀 때, 피보나치 수를 구하는 문제를 이 클로저 함수를 이용해서 풀었지만

함수의 호출이 끝났을 때 함수와 관련된 내부 변수들이 제거되는 것으로 알고 있어서, makeFib 안의 num1, num2가 왜 계속 기억되는지 제대로 이해하지 못하고 일단 작동은 하니 pass me를 제출했었다.

이번에 해당 문제를 예시로 클로저에 대해 좀 더 자세히 알아보려고 한다.

 

/* fn을 호출할 때마다 호출한 횟수 n에 해당하는 n번째의 피보나치 수를 출력 */


const makeFib = () => {
  let num1 = 0;  // 첫번째 숫자 저장
  let num2 = 1;  // 두번째 숫자 저장

  return function () {
    let tmpNum1 = num1;  // 아래에서 사용하기 위해 임시저장
    let tmpNum2 = num2;  // 아래에서 사용하기 위해 임시저장
    num1 = num2;
    num2 = tmpNum1 + tmpNum2;
    return tmpNum1;
  }
}

const fn = makeFib();

 

- 위의 함수가 작동되는 순서는: 

   1. 맨 아랫줄의 fn을 실행하면 makeFib의 내부함수인 익명함수가 실행

   2. 익명함수는 외부함수인 makeFib의 변수 num1, num2에 접근해 연산한 뒤, 피보나치 수를 출력

 

 - 여기에서 num1과 num2가 왜 계속 기억되어서 호출할 때마다 순서대로 피보나치 수를 출력하게 되는지가 궁금했음

 - 이는 렉시컬 스코프와, 함수가 가지고 있는 [[Environment]] 프로퍼티로 설명할 수 있었다

 

 - 렉시컬 스코프: lexical, 어휘적인 =>  함수가 작성될 때 결정되는 스코프

 

 

1. fn을 호출할 때마다 새로운 렉시컬 환경 객체가 만들어진다.

  - 렉시컬 환경 객체: 내부 렉시컬 환경(모든 지역변수 정보와 this 정보 등을 담고 있는 객체)

                                +

                                외부 렉시컬 환경(외부 코드와 연관, 여기서는 외부함수인 makeFib를 나타냄)

2. 모든 함수는 함수가 생성된 곳의 렉시컬 환경을 기억해, 이에 대한 참조가 [[Environment]] 라는 숨김 프로퍼티에 저장되고 fn은 생성된 곳의 렉시컬 환경을 참조하는 숨김 프로퍼티 [[Environment]] 를 갖는다.

   - [[Environment]] 는 함수가 생성될 때 딱 한 번 세팅된다.

3. fn은 호출될 때마다 [[Environment]] 에 접근하고, num1과 num2의 값을 수정해 저장한다.

4. 함수의 호출이 끝나면 렉시컬 환경이 제거되며 num1과 num2도 원래대로라면 제거되어야 하지만, [[Environment]] 에 렉시컬 환경에 대한 정보가 저장되어있기 때문에 도달 가능한 상태가 되어 그 값들이 유지된다.

     - 자바스크립트에서 모든 객체는 도달가능한 상태라면 메모리에 유지된다. (가비지 컬렉션 참고하기)

 

 

 - [[Environment]] 객체에 정확히 어떤 것들이 들어 있는지 알아보려고 직접 콘솔에 찍어 보았다.

 

fn을 생성했을 때

 

 - fn( ) 을 생성한 뒤 콘솔에 fn을 찍어 보니, [[Environment]] 객체는 없고, 대신 [[Scopes]] 객체가 있다.

 - [[Scopes]] 객체 안에 Closure와 관련된 항목이 있고, fn의 외부함수인 makeFib의 지역변수들이 저장되어 있었다.

 

 

fn을 1번 실행했을 때

 

fn을 2번 실행했을 때

 

 - [[Scopes]].Closure 안의 num1, num2 값들이 fn을 호출할 때마다 바뀌고 있는 것을 확인할 수 있었다.

 - [[Environment]] 는 Environment Records와 같은 의미이고, [[Scopes]] 프로퍼티는 말 그대로 함수의 클로저를 포함한 스코프 정보를 저장하는 객체이다.

 - 결과적으로 Environment Record의 내용들이 Scope에 표시되는 (것이 맞을까?) [[Environment]]와 [[Scope]] 는 fn의 렉시컬 환경을 저장한다는 점에 있어서 비슷한 역할을 하는 객체라고 판단했다.

 

(더 알아봐야겠다.. 이 두 가지가 같은 것을 가리키지만 단순히 용어의 차이인지, 아니면 서로 포함하는 범위가 조금씩 다른 객체고, 그 다른 부분이 무엇인지 잘 모르겠다)

(렉시컬 환경 부분 그림 추가하기)

(javascript environment records 키워드로 검색해보기)

 

 

 

 

참고:

https://ko.javascript.info/closure

https://poiemaweb.com/js-execution-context

https://stackoverflow.com/questions/37491626/where-does-a-javascript-closure-live

 

 

 

'JavaScript' 카테고리의 다른 글

ES6 class, super  (0) 2020.05.08
Object Oriented Programming  (0) 2020.05.08
이벤트 버블링과 캡쳐  (0) 2020.03.26
function method, this  (0) 2020.03.18
rest parameter, arguments, spread 연산자  (0) 2020.03.03