[240401] 23장 클로저 (2)
23장 클로저 (2)
클로저의 활용
- 상태를 안전하게 변경하고 유지하기 위해 사용
- 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용
- 특정 함수에게만 상태 변경을 허용하여 안전하게 유지 가능
const increase = (function () {
// 카운트 상태 변수
let num = 0;
return function() {
// 카운트 상태를 1만큼 증가시킨다.
return ++num;
}
}());
console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3
// 함수를 인수로 전달받고 함수를 반환하는 고차 함수
// 이 함수는 카운트 상태를 유지하기 위한 자유 변수 couter를 기억하는 클로저를 반환.
function makeCounter(aux) {
let counter = 0;
return function () {
// 인수로 전달받은 보조 함수에 상태 변경을 위임한다.
counter = aux(counter);
return counter;
};
}
// 보조 함수
function increase(n) {
return ++n;
}
function decrease(n) {
return --n;
}
const increaser = makeCounter(increase);
console.log(increaser()); // 1
console.log(increaser()); // 2
// increaser 함수와는 별개의 독립된 렉시컬 환경을 갖기 때문에 카운터 상태가 연동되지 않음
const decreaser = makeCounter(decreaser);
console.log(decreaser()); // -1
console.log(decreaser()); // -2
캡슐화와 정보 은닉
const Person = (function () {
let _age = 0; // private
// 생성자 함수
function Person(name, age) {
this.name = name; // public
_age = age;
}
// 프로토타입 메서드
Person.prototype.sayHi = function() {
console.log(`Hi ! My name is ${this.name}. I am ${_age}.`);
};
// 생성자 함수를 반환
return Person;
}());
const me = new Person('Lee', 20);
me.sayHi(); // Hi! My name is Lee. I am 20.
console.log(me.name); // Lee
console.log(me._age); // undefined
const me = new Person('Kim', 30);
me.sayHi(); // Hi! My name is Kim. I am 30.
console.log(me.name); // Kim
console.log(me._age); // undefined
- 현재는 클래스에 private 필드 정의 가능
자주 발생하는 실수
// Bad
var funcs = [];
for(var i = 0; i < 3; i++) {
funcs[i] = function () { return i; };
}
for(var j = 0; j < funcs.length; j++) {
console.log(funcs[j]()); // 3 3 3
}
- var 로 선언하면 i 가 전역이기 때문에 예상했던 0 1 2 가 나오지 않음
- let 키워드 사용하면 원하는 대로 나옴
// 클로저 사용
var funcs = [];
for(var i = 0; i < 3; i++) {
funcs[i] = (function (id) {
return function () {
return id;
};
}(i));
}
for(var j = 0; j < funcs.length; j++) {
console.log(funcs[j]());
}
const funcs = [];
for(let i = 0; i < 3; i++) {
funcs[i] = function () { return i; };
}
for(let j = 0; j < funcs.length; j++) {
console.log(funcs[j]()); // 0 1 2
}
- for 문의 변수 선언문에서 let 키워드로 선언한 초기화 변수를 사용한 for 문이 평가되면 먼저 새로운 렉시컬 환경을 생성하고 초기화 변수 식별자와 값을 등록 , 렉시컬 환경을 현재 실행중인 컨텍스트의 렉스컬 환경으로 교체
- 2,3,4 for 문의 코드 블록이 반복 실행되면 새로운 렉시컬 환경을 생성하고 식별자 값을 등록함 새롭게 생성된 렉시컬 환경 교체
- for 문이 종료되면 이전의 렉시컬 환경으로 돌아감
→ let 이나 const 키워드를 사용하는 반복문은 코드 블록을 반복 실행할 때마다 새로운 렉시컬 환경을 생성하여 반복할 다아시의 상태를 스냅숏 찍는 것처럼 저장한다.
Leave a comment