클로저
자바스크립트 엔진은 함수를 어디서 호출했는지가 아니라 함수를 어디에 정의했는지에 따라 상위 스코프를 결정한다. 이를 렉시컬 스코프(정적 스코프)라 한다.
렉시컬 환경의 "외부 렉시컬 환경에 대한 참조"에 저장할 참조값, 즉 상위 스코프에 대한 참조는 함수 정의가 평가되는 시점에 함수가 정의된 환경(위치)에 의해 결정된다. 이것이 바로 렉시컬 스코프다.
클로저와 렉시컬 환경
외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있다. 이러한 중첩 함수를 클로저라 부른다.
const x = 1;
function outer(){
const x = 10;
const inner = function(){ console.log(x);};
return inner;
}
//outer 함수를 호출하면 중첩 함수 inner를 반환한다.
//그리고 outer 함수의 실행 컨텍스트는 실행 케텍스트 스택에서 팝되어 제거된다.
const innerFunc = outer();
innerFunc();
클로저는 중첩 함수가 상위 스코프의 식별자를 참조하고 있고 중첩 함수가 외부 함수보다 더 오래 유지되는 경우에 한정하는 것이 일반적이다.
활용
클로저는 상태를 안전하게 변경하고 유지하기 위해 사용한다. 상태가 의도치 않게 변경되지 않도록 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하기 위해 사용한다.
//1. 상태 관리
//1.1. 일반
let num = 0;
//카운트 상태 변경 함수
const increase = function(){
//카운트 상태를 1만큼 증가시킨다.
return ++num;
};
console.log(increase()); //1
console.log(increase()); //2
console.log(increase()); //3
//1.2. 클로저인 메서드를 갖는 객체 반환
//은닉하고 특정 함수에게만 상태 변경을 허용
const counter = (function(){
//카운트 상태 변수
let num = 0;
//클로저인 메서드를 갖는 객체를 반환한다.
//객체 리터럴은 스코프를 만들지 않는다.
//따라서 아래 메서드들의 상위 스코프는 즉시 실행 함수의 렉시컬 환경이다.
return{
increase(){
return ++num;
},
decrease(){
return num > 0 ? --num : 0;
}
}
}());
console.log(counter.increase()); //1
console.log(counter.increase()); //2
console.log(counter.decrease()); //1
console.log(counter.decrease()); //0
//1.3. 생성자 함수로 표현
const counter = (function () {
//카운트 상태 변수
let num = 0;
function Counter(){}
Couter.prototype.increase = function(){
return ++num;
};
Counter.prototype.decrease = function(){
return num > 0 ? --num : 0;
};
return Counter;
}());
const counter = new Counter();
console.log(counter.increase()); //1
console.log(counter.increase()); //2
console.log(counter.decrease()); //1
console.log(counter.decrease()); //0
//1.4.함수형 프로그래밍에서 클로저를 활용
//1.4.1.렉시컬 환경 중복 생성
//함수를 인수로 전달받고 함수를 반환하는 고차 함수
//이 함수는 카운트 상태를 유지하기 위한 자유 변수 counter를 기억하는 클로저를 반환한다.
function makecouter(predicate){
//카운트 상태를 유지하기 위한 자유 변수
let counter = 0;
//클로저를 반환
return function(){
//인수로 전달받은 보조 함수에 상태 변경을 위임한다.
counter = predicate(counter);
return counter;
};
}
//보조함수
function increase(n){
return ++n;
}
//보조함수
function decrease(n){
return --n;
}
//함수로 함수를 생성한다.
//makeCounter 함수는 보조 함수를 인수로 전달받아 함수를 반환한다.
const increaser = makeCounter(increase);
console.log(increaser()); //1
console.log(increaser()); //2
//increaser 함수와는 별개의 독립된 렉시컬 환경을 갖기 때문에 카운터 상태가 연동하지 않는다.
const decreaser = makeCounter(decrease);
console.log(decreaser()); //-1
console.log(decreaser()); //-2
//1.4.2.렉시컬 환경 하나 생성
//함수를 반환하는 고차 함수
//이 함수는 카운트 상태를 유지하기 위한 자유 변수 counter를 기억하는 클로저를 반환한다.
const counter = (function(){
//카운트 상태를 유지하기 위한 자유 변수
let counter = 0;
//클로저를 반환
return function () {
//인수로 전달받은 보조 함수에 상태 변경을 위임한다.
counter = predicate(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
캡슐화
//1.캡슐화
function Person(name, age){
this.name = name; //public
let _age = age; //private
//인스턴스 메서드
this.sayHi = function(){
console.log(`Hi! My name is ${this.name}. I am ${_age}.`);
};
}
const me = new Person('Kim', 20);
me.sayHi(); //Hi! My name is Kim. I am 20.
console.log(me.name);//Kim
console.log(me.age);//undefined
const you = new Person('Lee', 30);
you.sayHi(); //Hi! My name is Lee. I am 30.
console.log(you.name);//Lee
console.log(you.age);//undefined
//2. 프로토타입 메서드 사용은 정보 은닉을 지원하지 않는다.
const Person = (function(){
let _age = 0; //private
//생성자 함수
function Person(name, age){
this.name = name;
_age = age;
}
//프로토타입 메서드
Person.prototype.sayHi = function(){
console.log(`Hi! My name is ${this.name}. I am ${_age}.`);
}
//생성자 함수를 반환
return Person;
}());
const me = new Person('Kim', 20);
me.sayHi(); //Hi! My name is Kim. I am 20.
console.log(me.name);//Kim
console.log(me._age);//undefined
const you = new Person('Lee', 30);
you.sayHi(); //Hi! My name is Lee. I am 30.
console.log(you.name);//Lee
console.log(you._age);//undefined
me.sayHi();
'Javascript > javascript Core' 카테고리의 다른 글
[Javascript강의]16강.AJAX (0) | 2021.10.17 |
---|---|
[Javascript강의]15강.이벤트 (0) | 2021.10.12 |
[Javascript 강의]13 강 실행 컨텍스트 (0) | 2021.10.10 |
[Javascript강의]12강 클래스 (0) | 2021.10.09 |
[Javascript 강의] 11강 this (0) | 2021.10.09 |