준비 운동
js 엔진은 함수가 호출될 때,
1) 함수를 호출한 위치가 아니라 정의한 위치(렉시컬 스코프) 에 따라 상위 스코프를 결정하고,
2) 상위 스코프를 내부슬롯에 저장.
클로저? 어디에 써?
변수 직접 접근변경을 막는 것과 같이 상태를 은닉해서, 특정 함수에게만 상태 변경을 허용하기 위해 사용.
클로저(Closure)란?
- 중첩 함수에서 함수가 종료된 이후에도, 내부 함수가 외부 함수의 변수를 참조하게 만든다는 거.
- 몬소리야? 외부 함수가 실행 종료된 이후, 외부 함수의 변수에 어떻게 접근 가능한가?
몬소리야. 어떻게?
1) 실행 컨텍스트 2) 렉시컬 스코프 생성/기억
- 상위 스코프(외부 함수)의 변수를 참조하던 내부 함수를 return
- 외부 함수는 .... 죽어도, 유산은 남아있는 거. 어디에? 내부 함수의 슬랏에.
- 즉, 외부 함수의 종료는 실행 컨텍스트에서 제거되는 것일 뿐, 렉시컬 환경은 남아 있음. --> 중첩 함수에서 기억한 외부 렉시컬 환경을 이용해서, 외부 변수를 기억하고, 접근 가능.
팁
console.dir(함수) 로 프로퍼티 들여다보기. : 내부슬롯에 [[Scope]] 항목에 저장된 Closure 확인 가능
24. 클로저 (DeepDive)
24.1 렉시컬 스코프
24.2 함수 객체의 [[Environment]]
24.3 클로저와 렉시컬 환경
24.4 클로저의 활용( ★★★ )
1) 전역 변수 변경 문제 -> 지역변수로
// 1. increase 함수만 num 변수를 참조하고 변경할 수 있게 만들기(전역변수 num X)
const increase = function () {
let num = 0; // 카운트 상태 변수
return ++num;
};
console.log(increase());
console.log(increase());
console.log(increase());
// 전역 변수는 지역 변수로 바꿨지만, num 값이 유지 안된다. ---> 함수가 종료되도, 변수값은 기억하고 있도록...가비지 컬렉터가 못지우도록-->클로저!
2. 클로저 만들기
전역 변수는 지역 변수로 바꿨지만, num 값이 유지 안된다. ---> 함수가 종료되도, 변수값은 기억하고 있도록...가비지 컬렉터가 못지우도록-->클로저!
// 2. 클로저 만들기
// 클로저는 상위 실행 스코프인 *즉시 실행 함수의 *렉시컬 환경을 기억.
const increase = (function () { // 반환받은 함수: 상위 스코프의 렉시컬 환경을 기억하는 Closure
let num = 0; // 함수가 종료되도, 내부 함수에서 num 을 참조하고 있어서, 렉시컬 환경은 유지(변수값 기억)
return function () {
return ++num; // 외부변수(상위스코프) num 참조
};
}());
console.log(increase());
console.log(increase());
console.log(increase());
// 즉시 실행 함수는 클로저 반환 -> memo 에 할당
// 클로저는 상위 실행 스코프인 *즉시 실행 함수의 *렉시컬 환경을 기억.
const memo = (function() {
let text = "메모";
return function () {
return (text = text + " 내용 추가");
};
}());
console.log(memo());
console.log(memo());
console.log(memo());
3. 카운트 감소시키기 추가
// 3. 감산기능도 추가해서, counter 로 업글
const counter = (function () { // 반환받은 함수: 상위 스코프의 렉시컬 환경을 기억하는 Closure
let num = 0; // 함수가 종료되도, 내부 함수에서 num 을 참조하고 있어서, 렉시컬 환경은 유지(변수값 기억)
// return function () {
// return ++num; // 외부변수(상위스코프) num 참조
// };
return {
increase() {
return ++num;
},
decrease() {
return num > 0 ? --num : 0;
}
};
}());
console.log(counter.increase());
console.log(counter.increase());
console.log(counter.decrease());
console.log(counter.decrease());
4. 감산 함수를 생성자 함수로 표현
const Counter = (function() {
let num = 0;
function Counter() {
} // this.num = 0; 안하는 이유? 프로퍼티는 public. 은닉X
Counter.prototype.increase = function () {
return ++num;
};
Counter.prototype.decrease = function () {
return num > 0 ? --num : 0;
};
return Counter;
}());
const counter = new Counter();
console.log(counter.increase());
console.log(counter.decrease());
5-1. 함수형 프로그래밍 : makeCounter 2번 호출 -> 렉시컬 환경 각각 생성. counter 공유 안됨
외부에서 상태 변경X. 불변성 지향. 고차 함수 ( 반환 뿐 아니라, 인수도 함수로 받기. ),
function makeCounter(aux) {
let counter = 0;
// 클로저 반환
return function () {
counter = aux(counter); // 인수로 전달받은 보조함수에 상태 변경을 위임.
return counter;
};
}
// 보조 함수
function increase(n) {
return ++n;
}
function decrease(n) {
return --n;
}
// 보조함수 increase, decrease 를 전달받아 함수를 반환한다.
const increaser = makeCounter(increase);
console.log(increaser());
console.log(increaser());
const decreaser = makeCounter(decrease);
console.log(decreaser());
console.log(decreaser());
6-2 자유 변수 공유
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));
console.log(counter(increase));
// 자유 변수 공유
console.log(counter(decrease));
console.log(counter(decrease));
24.5 캡슐화와 정보 은닉
24.6 자주 발생하는 실수
* 코딩앙마 강의
// Closure 함수와 렉시컬 환경의 조합. 함수가 생성될 당시의 환경을 기억.
// 외부 함수의 소멸 이후에도, 외부 함수의 변수에 접근
// 자바스크립트 함수는 렉시컬/정적 스코프(생성되면서 정의된 위치를 기억)
function makeAdder(x){
return function(y){ // 상위 함수인 makeAdder의 매개변수 x 에 접근 가능
return x + y;
}
}
const add3 = makeAdder(3);
console.log(add3(2)); // 5 , add3 함수가 생성된 이후에도 상위함수인 makeAdder의 x 에 접근 가능
const add10 = makeAdder(10);
console.log(add10(2)); // 15
console.log(add3(1)); // 4 상위 함수인 makeAdder(3) 함수가 생성될 당시의 <외부 변수>를 기억해서, 계속 접근 가능
은닉화
function makeCounter() {
let num = 0;
return function () {
return num++;
};
}
let counter = makeCounter();
console.log(counter()); // 0
console.log(counter()); // 1
console.log(counter()); // 2
'자바스크립트 문법 > 중고급(비동기_프라미스_this)' 카테고리의 다른 글
제너레이터 (0) | 2022.10.31 |
---|---|
비동기 promise, async/await (생활코딩) (0) | 2022.10.28 |
this, 콜백 ::: 코어 자바 스크립트 (0) | 2022.10.20 |
22장 this (0) | 2022.09.30 |
댓글