렉시컬 환경(Lexical environment)
렉시컬 환경은 envirionment record와 outer environment reference로 구성되어 있다.
- environment record : 지역(변수, 함수) 식별자 덩어리, 유효범위 내의 식별자가 어떤 값과 바인딩되어 있는지 변수객체를 관리해준다.
- outer environment reference : 자바스크립트는 함수 단위 유효범위를 갖는데, 이 함수들은 중첩될 수 있고 그에 따른 유효범위도 중첩될 수 있다. 유효범위가 중첩되었을 때 상위 유효범위로 올라갈 수 있어야 하는데 상위 유효범위에 대한 참조를 할 수 있다.
클로저(Closure)
클로저는 자바스크립트 고유의 개념이 아니라 함수를 일급 객체로 취급하는 함수형 프로그래밍 언어에서 사용되는 중요한 특성이다.
MDN의 클로저 정의
클로저는 “함수”와 그 “함수가 선언됐을 때의 렉시컬 환경(Lexical environment)”과의 조합이다.
내부함수가 외부함수의 스코프에 접근할 수 있게 한다.
/* outer 에서 inner 반환 */
function outer() {
var x = 10;
var inner = function () { console.log(x); };
return inner;
}
var closure = outer();
closure(); // 10
외부함수 outer()는 내부함수 inner()를 반환한다. 반환된 내부함수는 외부함수의 x라는 지역변수를 참조한다. 함수는 반환되는 순간 콜스택(실행 컨텍스트 스택)에서 제거되어 생을 마감한다. 따라서 closure() 함수를 실행했을 때 outer()는 이미 죽은 상태인데도 실행결과가 x의 값인 10이다. 왜냐하면 return inner라는 코드가 변수 x를 기억하는 클로저를 반환했기 때문이다. 클로저란 외부함수가 소멸한 뒤 외부함수 밖에서 내부함수가 호출되더라도 외부함수의 지역 변수에 접근할 수 있는 함수를 말한다.
클로저의 정의 클로저는 “함수”와 “그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)”과의 조합이다. 에서 “함수”란 반환된 내부함수를 의미하고 “그 함수가 선언될 때의 렉시컬 환경”이란 내부 함수가 선언됐을 때의 스코프를 의미한다. 즉, 클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수를 말한다.
실행 컨텍스트의 관점에서, 내부함수가 유효한 상태에서 외부함수가 종료하여 외부함수의 실행 컨텍스트가 반환되어도, 외부함수 실행 컨텍스트 내의 활성 객체(Activation object)(변수, 함수 선언 등의 정보를 가지고 있다)는 내부함수에 의해 참조되는 한 유효하여 내부함수가 스코프 체인을 통해 참조할 수 있는 것을 의미한다.
즉 외부함수가 이미 반환되었어도 외부함수 내의 변수는 이를 필요로 하는 내부함수가 하나 이상 존재하는 경우 계속 유지된다. 이때 내부함수가 외부함수에 있는 변수의 복사본이 아니라 실제 변수에 접근한다는 것에 주의하여야 한다.
function print(x) {
var text = x;
return function() {
console.log(text);
};
}
var closure1 = print('abc'); // text의 값이 'abc'인 렉시컬 환경을 저장
var closure2 = print('xyz'); // text의 값이 'xyz'인 렉시컬 환경을 저장
closure1(); // abc
closure2(); // xyz
closure1과 closure2는 선언 당시의 렉시컬 환경을 각각 저장한다. closure1과 closure2의 text는 각각 생성되었고, 클로저는 자신이 생성된 시점의 환경을 기억하였다가 호출되었을 때 기억해 놓은 환경을 가지고 와서 수행해 준다.
클로저의 활용
클로저가 가장 유용하게 사용되는 상황은 현재 상태를 기억하고 변경된 최신 상태를 유지하는 것이다.
<!DOCTYPE html>
<html>
<body>
<button id='button'>0</button>
<script>
var btn = document.getElementById('button');
const btnClick = (function() {
var count = 0;
return function() { // 클로저 반환
count += 1; // 상태 변경
btn.innerText=count;
}
})();
btn.onclick = btnClick; // 이벤트 프로퍼티에 클로저 할당
</script>
</body>
</html>
버튼을 클릭하면 count가 증가하는 코드이다.
만약 count를 전역 변수로 설정하면 어디에서나 접근하여 변경이 가능해져 의도치 않은 값의 변경이 일어날 수 있다. count를 지역 변수로 설정하면 함수를 호출할 때마다 count 값이 0으로 초기화되어버린다.
위의 예제는 클로저를 사용한 것으로, 즉시실행함수 btnClick()은 호출 후 바로 소멸하지만 즉시실행함수 내부의 함수가 클로저가 되면서 렉시컬 환경(count)을 기억하게 된다. 그래서 클로저에서는 즉시실행함수의 지역변수인 count에 접근할 수 있게 되고, 외부에서는 count에 직접 접근이 불가능하게 되어 private 하게 사용할 수 있다.
'자바스크립트' 카테고리의 다른 글
| 동기적 실행 vs 비동기적 실행 (0) | 2023.03.28 |
|---|---|
| 프로토타입 체이닝 (상속 흉내) (0) | 2023.03.24 |
| this (0) | 2023.03.17 |
| callback, promise, async/await (0) | 2023.03.14 |
| 이벤트 위임 (0) | 2023.03.11 |