2020. 4. 28. 11:41ㆍJavaScript
* 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을 찍어 보니, [[Environment]] 객체는 없고, 대신 [[Scopes]] 객체가 있다.
- [[Scopes]] 객체 안에 Closure와 관련된 항목이 있고, fn의 외부함수인 makeFib의 지역변수들이 저장되어 있었다.
- [[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 |