클로저
정의 - 함수와 함수가 선언된 렉시컬 환경과의 조합
렉시컬 스코프
렉시컬 환경의 “외부 렉시컬 환경에 대한 참조”에 저장할 참조값, 즉 상위 스코프에 대한 참조는 함수 정의가 평가되는 시점에 함수가 정의된 환경(위치)에 의해 결정된다. 이것이 바로 렉시컬 스코프다.
함수 객체의 내부 슬롯
함수가 정의된 환경(위치)과 호출되는 환경(위치)은 다를 수 있다.
함수는 자신의 내부 슬롯 Environment에 자신이 정의된 환경, 즉 상위 스코프의 참조를 저장한다.
클로저와 렉시컬 환경
외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있다. 이러한 중첩 함수를 클로저closure라고 부른다.
const x = 1;
// ①
function outer() {
const x = 10;
const inner = function () { console.log(x); }; // ②
return inner;
}
// outer 함수를 호출하면 중첩 함수 inner를 반환한다.
// 그리고 outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 팝되어 제거된다.
const innerFunc = outer(); // ③
innerFunc(); // ④ 10
outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 제거되지만 outer 함수의 렉시컬 환경까지 소멸하는 것은 아니다.
대부분의 모던 브라우저에서 클로저는 상위 스코프의 식별자중에 참조하고 있는 식별자만을 기억한다.
자유변수 클로저에 의해 참조되는 상위 스코프의 변수
클로저의 활용
클로저는 상태state를 안전하게 변경하고 유지하기 위해 사용한다.
상태가 의도치 않게 변경되지 않도록 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하기 위해 사용한다.
// 함수를 반환하는 고차 함수
// 이 함수는 카운트 상태를 유지하기 위한 자유 변수 counter를 기억하는 클로저를 반환한다.
const counter = (function () {
// 카운트 상태를 유지하기 위한 자유 변수
let counter = 0;
// 함수를 인수로 전달받는 클로저를 반환
return function (aux) {
// 인수로 전달받은 보조 함수에 상태 변경을 위임한다.
counter = aux(counter);
// 카운트 상태 변경 함수
return counter;
};
}());
// 보조 함수
function increase(n) {
return ++n;
}
// 보조 함수
function decrease(n) {
return --n;
}
// 보조 함수를 전달하여 호출
console.log(counter(increase)); // 1
console.log(counter(increase)); // 2
// 자유 변수를 공유한다.
console.log(counter(decrease)); // 1
console.log(counter(decrease)); // 0
캡슐화와 정보 은닉
JS 객체의 모든 프로퍼티와 메서드는 기본적으로 public 하지만 대부분 사용 가능한 걸. (대부분의 브라우저, node 12이상)
기타 방법
클래스의 getter, setter로 회피 -> privateClass.js, privateClass02.js
모듈화로 회피 -> test.html, outer.js, inner.js
TS를 쓰면 public, private, protected를 지원해서 해결!
자주 발생하는 실수
const funcs = [];
for (let i = 0; i < 3; i++) {
funcs[i] = function () { return i; };
}
for (let i = 0; i < funcs.length; i++) {
console.log(funcs[i]()); // 0 1 2
}
for 문의 변수 선언문에서 let 키워드로 선언한 변수를 사용하면 for 문의 코드 블록이 반복 실행될 때마다
for 문 코드 블록의 새로운 렉시컬 환경이 생성된다.
고차함수를 이용한 방법
// 요소가 3개인 배열을 생성하고 배열의 인덱스를 반환하는 함수를 요소로 추가한다.
// 배열의 요소로 추가된 함수들은 모두 클로저다.
const funcs = Array.from(new Array(3), (_, i) => () => i); // (3) [ƒ, ƒ, ƒ]
// 배열의 요소로 추가된 함수들을 순차적으로 호출한다.
funcs.forEach(f => console.log(f())); // 0 1 2
'공부공부 > JS 딥다이브' 카테고리의 다른 글
[js 딥다이브] 26장 ES6 함수의 추가 기능 (0) | 2024.02.15 |
---|---|
[js 딥다이브] 25장 클래스 (0) | 2024.02.15 |
[js 딥다이브] 23장 실행 컨텍스트 (0) | 2024.02.15 |
[js 딥다이브] 22장 this (0) | 2024.02.15 |
[js 딥다이브] 21장 빌트인 객체 (0) | 2024.02.15 |