1. 정의(MDN)
MDN : A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment)
2. 렉시컬 환경(Lexical Environment)
특정 코드가 작성, 선언된 환경(장소)을 의미한다.
2.1 렉시컬의 구성
- EnvironmentRecord : 현재 실행중인 코드 환경의 this값과 선언된 모든 변수와 함수가 저장되는 곳
- OuterLexicalEnvironmentReference : 외부 렉시컬 환경의 참조값

2.2 실행 컨텍스트와 렉시컬의 관계

2.3 렉시컬과 스코프체인
- OuterLexical Environment Refernce를 통해 스코프 체이닝이 이뤄진다.
함수레벨 스코프
- var를 이용한 변수 선언
- local(지역)은 함수내부를 말하고, 이는 코드 블럭이 아닌
함수에 의해서만 지역 스코프
가 생성된다는 의미이다.
블럭 레벨 스코프
- let, const를 이용한 변수선언
- {} 내부안의 스코프(if,for,while,switch 등)
참고사항 => var는 배제하고, let이나 const를 이용해서 선언하자!!
렉시컬 스코프
- 동적 스코프(Dynamic Scope) : 함수 정의 위치에 따른 상위 스코프 결정방식
- 정적스코프(Static Scope==Lexical Scope) : 함수의 정의 위치에 따른 상위 스코프 결정 방식
자바스크립트는 정적스코프(Lexical Scope) 방식
을 따른다.
- 자바스크립트는 이를 구현하기 위해 함수객체의
내부슬롯[[Environment]]
에 상위스코프의 참조를 저장한다.
2.3.1 렉시컬 스코프에 대한 예제
const x=1;
function foo(){
const x=10;
bar();
}
function bar(){
console.log(x);
}
foo();
bar();
코드 평가 순서
- 함수 실행 컨텍스트 생성
- 함수 렉시컬 환경 생성
2.1 함수 환경 레코드 생성
2.2 this 바인딩
2.3 외부 렉시컬 환겨에 대한 참조 결정
 |
---|
내부슬롯[[Environment]]에 상위스코프가 저장된다. |
3. 클로저와 렉시컬 환경
const x=1;
function outer(){
const x=10;
const inner = function(){
console.log(x);
};
return inner;
}
const innerFunc=outer();
innerFunc();
3.1 전역평가 후
 |
---|
전역평가후 |
3.2 outer() 실행중(break line 9)
 |
---|
outer 실행중 |
3.3 inner 실행중(break line 10)
 |
---|
 |
inner 실행중 |
※ 중요사항
- inner함수에서 outer의 변수x를 사용
- 이경우 outer 실행 후(line 9) outer Execution Context는 상실하지만, outer함수의 렉시컬 환경은 유지된다.
- 이러한 상황의 inner를 통상적으로 클로저라 한다.
- 자바스크립트는 기본적으로 모든 함수는 클로저로 작동하지만, 통상적으로는 실행컨텍스트에서는 사라지고 렉시컬 환경은 유지되는 상황을 일컫는다.
4. 클로저의 활용
- 상태유지
- 전역변수 사용억제
- 고차함수의 활용
- 정보은닉
4.1 상태유지
현재 상태를 기억하고 변경된 최신 상태를 유지하는데 유용하다.
<html>
<body>
<button class="toggle">toggle</button>
<div class="box" style="width:100px; height:100px;background:red;"></div>
<script>
let box = document.querySelector('.box');
let toggleBtn = document.querySelector('.toggle');
let toggle=(function(){
let isShow=false;
return function(){
box.style.display = isShow ? 'block':'none';
isShow=!isShow;
};
})();
toggleBtn.onclick=toggle;
</script>
</html>
- isShow를 기억하고 변경한다.
- 클로저가 없으면 전역변수로 정의하고 사용해야 한다.
- 전역의 경우 누구나 접근, 변경할수 있으므로 많은 사이드이펙트를 유발한다.
- 이러한 경우 클로저가 유용하다.
4.2 전역변수 사용억제
<html>
<body>
<button id="increase">+</button>
<p id="count">0</p>
<script>
let increaseBtn = document.querySelector('#increase');
let count = document.querySelector('#count');
let increase=(function(){
let counter=0;
return function(){
return ++counter;
};
})();
increaseBtn.onclick=function(){
count.innerHTML=increase();
}
</script>
</body>
</html>
- 4.1과 유사한 상태
- 상태유지를 위해 전역이 아닌 클로저를 활용하여 임의로 수정불가
- increase를 이용해서만 수정가능
4.3 고차함수의 활용
- 고차 함수는 함수를 인자로 받거나 또는 함수를 반환함으로써 작동 하는 함수
- 일급객체로서의 함수
function makeCounter(predicate) {
var counter = 0;
return function () {
counter = predicate(counter);
return counter;
};
}
function increase(n) {
return ++n;
}
function decrease(n) {
return --n;
}
const increaser = makeCounter(increase);
console.log(increaser());
console.log(increaser());
const decreaser = makeCounter(decrease);
console.log(decreaser());
console.log(decreaser());
 |
---|
increase, decrease 렉시컬 환경 |
- counter가 각각의 렉시컬로 분리되어 있다
- 원인은 makeCounter 함수를 두번 호출해서 발생하는 문제 => 렉시컬 분리
- 원하는 방식은 증감함수가 하나의 변수를 변경하는 것이므로 아래와 같이 변경
<script>
const counter = (function(){
let count=0;
return function(predicate){
count=predicate(count);
return count;
}
}());
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));
</script>
 |
---|
count(increase), count(decrease) 렉시컬 환경 |
4.4 정보은닉