클로저
함수를 일급 객체로 취급하는 함수형 프로그래밍 언어(하스켈,리스프,얼랭 등) 에서 사용되는 중요한 특성이다.
*MDN 클로저 정의: 클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.
함수 중첩
const x=1;
function outerFunc(){
const x=10;
function innerFunc(){
console.log(x) //10
}
innerFunc()
}
outerFunc()
-------------------------------
중첩 X
const x=11;
function outerFunc(){
const x=5;
innerFunc()
}
function innerFunc(){
console.log(x) //11
}
outerFunc()
렉시컬 스코프
-함수를 어디에 정의했는지에 따라 상위 스코프를 결정하고 이를 렉시컬 스코프(정적 스코프) 라고 한다.
const x=1;
function foo(){
const x=10;
bar()
}
function bar(){
console.log(x);
}
foo() => 1
bar() => 1
foo() , bar() 의 상위 스코프는 전역스코프 / 스코프에 실체는 실행 컨텍스트의 렉시컬 환경이다.
중요 개념
- 외부 렉시컬 환경에 대한 참조를 통해 상위 렉시컬 환경과 연결 [ 스코프 체인 ]
- 함수의 상위 스코프 결정 == 렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 저장할 참조값을 결정한다.
- 렉시컬 환경의 "외부 렉시컬 환경에 대한 참조"에 저장할 참조값 [ 상위 스코프 ]
상위 스코프에 대한 참조는 함수 정의가 평가되는 시점에 함수가 정의된 위치(환경)에 의해 결정된다.
함수 객체의 내부 슬롯 [[Environment]]
함수가 정의된 환경(위치) 와 호출되는 환경(위치)은 다를 수 있다.
함수는 자신의 내부 슬롯 [[Environment]]에 자신이 정의된 환경, 즉 상위 스코프의 참조를 저장한다.
이 상위 스코프의 참조는 현재 실행중인 실행 컨텍스트의 렉시컬 환경을 가리킨다
함수 객체의 내부 슬롯 [[Environment]]에 저장된 현재 실행중인 실행 컨텍스트의 렉시컬 환경의 참조가 상위 스코프
==
자신이 호출되었을 때 생성될 함수 렉시컬 환경의 "외부 렉시컬 환경에 대한 참조"에 저장될 참조값이다.
앞장에서 정리했던(렉시컬 스코프) 예제다

https://pro-train.tistory.com/73
this / 실행 컨텍스트
this this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수(self-referencing variable)다. this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를
pro-train.tistory.com
클로저와 렉시컬 환경
const x=1;
function outer(){
const x=10;
const inner = function() { console.log(x);}
return inner;
}
cosnt innerFunc=outer();
innerFunc(); //10
이런 경우 -> 이런 중첩함수를 클로저라고한다
외부 함수보다 중첩함수가 더 오래 유지되는 경우 중첩함수는 이미 생명주기가 종료한 외부함수의 변수를 참조할 수 있다
왜 10일까?
outer() 를 하면 inner라는 함수를 return 해주고 outer함수는 종료(실행 컨텍스트에서 pop 제거됨)
그러면 내부에 있는 지역변수 x도 함께 제거 (생명주기가 끝났기 때문에)되어야 하는데 그대로 값을 가지고 있다
-> outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 제거되지만 outer함수의 렉시컬 환경은 살아있다
이유) outer 함수 호출시 -> outer함수의 렉시컬 환경 생성 , outer함수의 [[Environment]] 내부 슬롯에 저장된 전역 렉시컬 환경을 outer함수 렉시컬 환경의 "외부 렉시컬 환경에 대한 참조"에 할당
중첩함수인 inner함수의 상위 스코프는 outer함수이고 그에 따라서 outer함수의 렉시컬 환경을 상위스코프로서 저장
return이 나오면서 outer함수의 실행이 종료될때 outer함수의 렉시컬 환경은 inner함수의 [[Environment]] 내부 슬롯에
참조되어 있고 inner함수는 innerFunc이라는 전역 변수에 참조 되어 있으므로 가비지 콜랙션의 대상이 되지 않아서




첫번째 예제는 bar 함수가 foo 함수보다 먼저 소멸하므로 클로저라고 할 수 없다 (외부 함수보다 먼저 죽음)
두번째 예제는 bar 함수가 클로져 였다가 바로 소멸하는 경우다 외부함수인 foo 보다 먼저 끝나므로 종료된 외부함수의 식별자를 참조할 수 있다는 클로져에 본질에 맞지 않는다
셋번째 예제는 bar 함수가 외부 식별자x를 참조한 상태에서 return 되서 외부함수의 생명주기가 끝나도 const bar에서 살아있다.
클로저는 중첩 함수가 상위 스코프의 식별자를 참조하고 있고 중첩함수가 외부 함수보다 더 오래 유지되는 경우에
한정하는 것이 일반적임
클로저에 의해 참조되는 상위 스코프의 변수 (위 예제의 x변수)는 자유 변수라고 불린다.
클로저의 활용
클로저는 상태를 안전하게 변경하고 유지하기 위해 사용한다.
상태가 의도치 않게 변경되지 않도록 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하기 위해 사용
let num=0;
const increase= function() {
return ++num;
}
console.log(increase())
console.log(increase())
console.log(increase())
1
2
3
위 코드는 오류가 발생할 수 있는 코드라서 좋지 않은 코드다 -> 전역 변수 num을 변경할 수 있기 때문
클로저
const increase= (function() {
let num=0;
return function(){
return ++num;
}
}());
이렇게 function전체를 묶어주는 ()를 제거해도 동일한 값이 나온다
const increase = function() {
let num=0;
return function(){
return ++num;
}
}();
console.log(increase()) 1
console.log(increase()) 2
console.log(increase()) 3
하지만 }()); 이부분에서 ()이걸 제거하면
ƒ (){
return ++num;
}이런식으로 출력된다.
()이걸 붙여놓은 이유는 즉시실행하기 위함 -> 즉시 실행 함수
즉시실행 함수는 호출된 이후 바로 소멸하지만 반환한 클로저는 increase 변수에 할당되어서 호출된다
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
이 예제를 생성자 함수로 표현해보자
const Counter=(function(){
let num=0;
function Counter(){
}
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());
함수형 프로그래밍의 클로저 예제
function makeCounter(aux){
let counter=0;
return fucntion (){
counter=aux(counter)
return counter
}
}
function increase(n){
return ++n;
}
function decrease(n){
return --n;
}
const increaser=makeCounter(increase)
console.log(increaser()) //1
increase 함수와는 별개의 독립된 렉시컬 환경을 갖기 때문에 카운터 상태가 연동하지 않는다
const decreaser=makeCounter(decrease)
console.log(decreaser()) //-1
카운터 상태 연동
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(decrease));
캡슐화와 정보은닉
캡슐화: 객체의 상태를 나타내는 프로퍼티와 프로퍼티를 참조하고 조작할 수 있는 동작인 메서드를 하나로 묶는 것
다른 언어에서는 protected,private,public같은 접근 제한자가 있지만 JS에는 없다
JS에서 사용하는 방법 변수 앞에 _ 붙이기
function Person(name,age){
this.name=name;
let _age=age; //private
}
*자주 발생하는 실수 부분은 읽고 넘어가자
'JavaScript' 카테고리의 다른 글
| ES6 함수의 추가 기능 (0) | 2022.06.22 |
|---|---|
| 클래스 (0) | 2022.06.19 |
| this / 실행 컨텍스트 (0) | 2022.06.09 |
| strict mode / 빌트인 객체 (0) | 2022.05.24 |
| 함수와 일급 객체 (1) | 2022.05.14 |