[자바스크립트] - 클로저 , [[Enviroment]]

#클로저?
클로저란 JavaSciprt만의 고유 개념이 아닌 함수형 프로그래밍 언어의 중요한 특성이다.
클로저에 대한 이해를 돕기위한 선행 지식으로 "스코프" , "[[Enviroment]]" , "렉시컬 환경" , "렉시컬 스코프"를 알아야 이해 하기가 편하다.
클로저에 대한 내 이해를 한줄로 요악하자면
"중첩함수의 개념으로 외부함수의 생명주기(ex. 실행컨텍스트)가 종료되어도 내부함수에서 외부함수 내 변수를 캡처하여 사용 할 수 있는 개념"
내부슬롯은 [[Enviroment]]은 상위 스코프의 렉시컬 환경을 참조한다.
렉시컬 환경은 함수가 실행됨에 따라
#함수객체의 내부 슬롯 [[Enviroment]]
내부슬롯이란 ECMASciprt JavaSciprt의 엔진의 동작의 이해를 돕기위한 개념에 대한 정의이다.
클로저의 핵심 개념으로 함수객체의 내부슬롯인 [[Enviroment]] 은 상위 렉시컬 환경을 참조함으로 내부 스코프에서 외부 스코프의 참조값을 가져올 수 있다.
const x = 1; //전역변수
function func(){ // 전역함수
return x;
}
console.log(func());
위 예제에서 func()는 전역변수이지만 내부는 함수 스코프를 가지고있다.
위 예제의 실행 컨텍스트는 아래와 같다.
- 전역 렉시컬 환경의 렉시컬 레코드에 x와 x의 참조값을 반영
- func() 함수의 내부 슬롯은 [[Enviorment]]에 상위 스코프인 전역 렉시컬 환경을 참조
- x는 지역변수가 아니기에 내부 슬롯을 통하여 전역 렉시컬 환경의 환경레코드 x를 참조
- 참조한 x를 console.log (1)을 출력
함수객체의 내부슬롯을 통하여 상위 스코프의 영역을 가져올 수 있는 외부 참조 변수가 되는 것이다.
클로저를 이해할때 "하위 스코프에서 상위스코프의 변수값을 가져오는구나~" 하고 이해하면 잘못 이해하고 있는 것이다.
위의 개념은 외부 참조 변수이지 클로저의 개념이 아니다. 클로저는 중첩함수에서만 작용되는 개념이며, 외부 함수 변수를 캡처하여
생명주기가 종료 되었음에도 값을 사용 할 수 있는 것이 클로저의 핵심 개념이다.
#클로저, 중첩함수?
그럼 종료되었을 때는 어느 경우가 있을지 중첩 함수의 예제를 보면 쉽게 이해가 가능하다.
const counter = () =>{
let num = 0;
return {
increment : () =>{
return ++num;
},
decrement : () =>{
return --num;
}
}
}
const result = counter();
result.increment();
counter 함수는 num이라는 변수를 가지고 있고 렉시컬 환경에 반영하게 되고
result 상수에 counter 함수를 실행한 리턴값을 받아온다. increment의 객체 메서드를 실행하게되면 이는 지역 변수에 num이 없기에 이미 종료된 num의 렉시컬 환경을 캡처하고 유효한 값으로 유지시킨다.
decrement 메서드 또한 increment 같이 내부에 클로저를 형성하는데 이건 같은 상위의 렉시컬 환경을 참조하고 있음으로 변수의 값을 공유하는 개념이 되기 때문에 num의 값이 유지가 되며 값을 변경할 수 있다.
물론 전역에서 num을 설정하여도 데이터는 유지되겠지만 문제점은 데이터의 은닉이 불가하다는 점이다. 전역변수에 특정 이유를 제외하고 유동하는 변수를 만들게되면 다른 함수나 개발자의 실수 등으로 인해 값이 내 의도와 다르게 변경이 될 수도 있다.
허나 클로저를 응용해서 위 예제처럼 구현한다면 함수 스코프로써 외부에서 접근 불가하기에 함수에서만 사용가능한 데이터 은닉(캡슐화)을 유지하는 카운팅 로직이 구현 가능하다.
함수 스코프 내에서 클로저를 응용해서 구현한 간단한 예제가 되는 것이다.
아래는 두번째 예시이다.
const timer = (Message ,delay)=>{
let timer = 0;
setInterval(()=>{
console.log(`${Message} ${++timer}`);
},delay);
}
timer('Timer 시작', 1000);
Timer는 Message('Timer 시작')과 delay(1000)을 받아 timer 렉시컬 환경을 구성 및 환경 레코드를 저장하고
setInterval은 클로저를 형성하여 Timer와 매개변수인 Message는 timer의 렉시컬 환경을 참조하여 실행한다.
delay는 setInterval의 두번째 매개변수로 직접 전달 되기에 상위 환경에서 참조 값을 가져오지 않는다.
Closer는 상위 스코프의 자유변수(Free variable)를 setInterval의 timer가 자유변수에 묶여있다 & 닫혀있다(closer) 라고
표현 한다. 프로그래밍 언어들은 클로저를 형성하여 재사용성을 늘리고, 정보은닉, 캡슐화등 여러 응용의 매개체가 되는 중요 개념 중 하나 이다.
참고문헌 : 모던 Deep Dive - JavaSipcrt p. 388 - 416 / 이웅모 지움
'JavaSciprt' 카테고리의 다른 글
| [자바스크립트] 동기 , 비동기 그리고 태스크 큐, 마이크로 태스크 큐 (0) | 2024.03.02 |
|---|---|
| [자바스크립트] 일급객체 (0) | 2024.01.15 |
| [자바스크립트] - Scope (1) | 2023.12.28 |