클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.
어디서 정의했는지에 따라 상위 스코프를 결정하는 것.
[[Environment]] 내부 슬롯
외부함수보다 중첩함수가 더 오래 유지될 때, 생명 주기 종료된 외부함수 변수 참조 가능하다. 이 때의 중첩함수를 클로저라고 한다.
상위 스코프 식별자 참조 필요가 없어 기억하지 않으면 클로저가 아니다.
외부함수가 더 오래 유지되면 일반적으로 클로저 아니다.
상위 스코프 중 본인이 참조할 즉 필요한 식별자만 기억한다.
클로저가 참조하는 상위 스코프의 변수: 자유 변수
클로저: 자유변수에 의해 묶여있는(closed) 함수
//Case1. num이 은닉 X 모든 함수가 접근 가능
let num = 0;
const increase = function () {
return ++num;
};
//Case2. num이 은닉 O increase 함수만 접근 가능
const increase = (function () {
let num = 0;
return function () {
return ++num;
};
})();
//increase 함수는 외부함수가 리턴한 중첩함수를 즉시 실행하는 함수이다. 즉 중첩함수 자체이다.
//Case3. 생성자 함수를 이용
const Counter = (function () {
let num = 0;
function Counter() {}
Counter.prototype.increase = function () {
return ++num;
};
Counter.prototype.decrease = function () {
return num !== 0 ? --num : num;
};
return Counter;
})();
const counter = new Counter();
//Case4. 함수형 프로그래밍
const increase = function (num) {
return ++num;
};
function makeCounter(predicate) {
let counter = 0;
return function () {
return (counter = predicate(counter));
};
}
const increaser = makeCounter(increase);
const decreaser = makeCounter(decrease);
console.log(increaser());
console.log(increaser());
//makeCounter함수가 반환하는 함수는 makeCounter안에서 정의되었으므로 counter을 자유 변수로 갖는다.
function Person(name, age) {
this.name = name;
let _age = age;
this.sayHi = function () {
console.log(`Hi I am ${this.name}(${_age}) `);
};
}
const me = new Person("Park", 22);
me.sayHi();
console.log(me.name, me._age);
const Person = (function () {
let _age;
function Person(name, age) {
this.name = name;
_age = age;
}
Person.prototype.sayHi = function () {
console.log(`Hi I am ${this.name}(${_age}) `);
};
return Person;
})();
const me = new Person("Park", 22);
me.sayHi();
console.log(me.name, me._age); //Park , undefined
const you = new Person("Lee", 27);
you.sayHi();
console.log(you.name, you._age); //Park , undefined
me.sayHi();
sayHi 함수를 prototype으로 교체하였더니, 인스턴스가 생성될 때마다 sayHi의 상위 스코프가 바뀌어 모든 인스턴스의 자유 변수인 age 값이 같이 바뀐다.
즉 JS에서는 캡슐화 && 같은 함수 1번만 사용 && 정보 은닉 === false
이었다.