1. 클로저의 의미 및 원리
-A closure is the combination of a function and the lexical environment within that function was declared. -MDN-
-클로저는 함수와 그 함수가 선언될 당시의 lexical environment의 상호관계에 따른 현상
-여기서 함수가 선언될 당시의 lexical environment는 lexical environment의 outerEnvironmentReference에 해당
[코어 자바스크립트] 02. 실행 컨텍스트
1. 실행 컨텍스트 1) 실행 컨텍스트: 실행할 코드에 제공할 환경 정보들을 모아놓은 객체 -선언된 변수, 함수 등을 실행 컨텍스트에 저장해두었다가 호출하면 나옴 2) 스택(stack): 우물 같은 구조로
data-science-study.tistory.com
-윗글을 참조하면 outerEnvironmentReference는 현재 컨텍스트 바깥의 컨텍스트에 접근할 수 있도록 바깥의 컨텍스트를 저장
ex) 컨텍스트 A → 내부에 존재하는 내부함수 B
▶내부함수 B의 실행 컨텍스트가 활성화된 시점에서 B의 outerEnvironmentReference가 A의 lexical environment를 참조하므로 A의 변수에 접근 가능
▶A는 B에서 선언한 변수에 접근 불가능
-예시에서 B가 A의 변수에 접근가능하여 사용하면 B와 A가 상호작용을 한 것이지만 A의 변수를 무조건 쓰지는 않으므로 상호작용 하지 않을 수도 있음
-즉, 내부함수에서 외부 변수를 참조하는 경우에 한해서만 combination이 발생
-결론적으로, 어떤 함수에서 선언한 변수를 참조하는 내부함수에서만 발생하는 현상
(컨텍스트 A에서 선언한 변수를 내부함수 B에서 참조하는 경우에 클로저 발생)
-예시 1)
var outer = function() {
var a = 1;
var inner = function() {
console.log(++a);
};
inner();
};
outer();
→outer 함수에서 변수 a를 선언
→outer 함수 내에서 inner 함수 선언
→inner 함수 내에서 a의 값을 1 증가시킨 후 출력
→inner 함수 내에 변수 a가 선언되지 않았으므로 environmentRecord에서 값을 찾지 못하고 outerenvironmentReference에 지정된 outer 함수의 environmentRecord에 접근하여 outer 함수에서 지정된 변수 a에 접근
→따라서, '2' 출력
-예시 2)
var outer = function() {
var a = 1;
var inner = function() {
return ++a;
};
return inner();
};
var outer2 = outer();
console.log(outer2);
→이번에는 outer 함수 내에서 inner() 함수의 실행 결과(2)를 반환
→outer 함수의 실행 컨텍스트가 종료된 시점 이후 a 변수와 inner 함수는 사라질 것
→outer2에는 inner 함수의 실행결과인 '2'가 저장되어, '2'가 출력되며 outer2를 이후에 반복해서 출력해도 '2'만 출력
-예시 3)
var outer = function() {
var a = 1;
var inner = function() {
return ++a;
};
return inner;
};
var outer2 = outer();
console.log(outer2());
console.log(outer2());
→inner 함수 자체를 반환하여 outer2에는 inner(a에 1씩 더한 값을 반환하는) 함수가 저장됨
→예시 2)에서 outer2 = 2였다면 예시 3)에서 outer2 = function() { return ++a }
→따라서, outer2()를 실행할 때마다 ++a, 즉 a에 1씩 더해져 반환되고 반복해서 실행하면 계속 1씩 더해진 값을 가질 수 있음
→outer 함수의 실행 종료 시점에서 inner 함수를 반환하고 inner 함수를 반환하고 나면 outer 함수는 종료된 상태
→이 때, inner 함수는 outer2에 저장되어 outer2를 실행하면 언제든 호출될 가능성이 열린 것이므로 inner 함수가 참조해야하는 outer 함수의 LexicalEnvironment는 가비지 콜렉터의 수집대상에서 제외됨
-예제 3)을 통해 알 수 있는 클로저의 정의
→어떤 함수 A(outer)에서 선언한 변수(a)를 참조하는 내부함수 B(inner)를 외부로 전달(outer2에 inner 함수 자체를 반환)할 경우, A의 실행 컨텍스트 종료 이후에도 변수가 사라지지 않는 현상
→이때, 외부로 전달이 return만 의미하는 것은 아님
-예시 4)
( function () {
var a = 0;
var intervalId = null;
var inner = function () {
if (++a >=10 ) {
clearInterval(intervalId);
}
console.log(a);
};
intervalId = setInterval(inner, 1000);
})();
→setInterval에 전달할 콜백 함수 inner는 inner 외부에서 선언된 변수 a를 참조
-예시 5)
( function () {
var count = 0;
var button = document.createElement('button');
button.innerText = 'click';
button.addEventListener('click', function () {
console.log(++count, 'times clicked');
});
document.body.appendChild(button);
})();
→'click'이라고 쓰인 버튼을 생성하고, 버튼을 클릭할 때마다 count가 1씩 더해진 값과 'times clicked' 문자가 함께 출력
→이때, addEventListener에서 실행할 함수 내부에서 외부에 있는 변수 count를 참조함
-예제 5), 6) 모두 지역변수(a, count)를 참조하는 내부함수(inner, function)를 외부(setInterval, button)으로 전달했기 때문에 클로저
2. 클로저와 메모리 관리
-클로저는 어떤 필요에 의해 의도적으로 함수의 지역변수가 메모리를 소모하도록(a가 사라지지 않고 계속 참조되도록 하는 등) 함으로써 발생
-필요성이 사라진 시점에서 메모리를 더 소모하지 않도록 해주면 됨
-참조 카운트를 0으로 만들어 더 이상 참조되지도 않고 쓰이지도 않게 된다면 메모리가 회수될 것
-이 때, 참조 카운트를 0으로 만드는 방법으로 null이나 undefined를 할당하면 됨
-예시 1)
var outer = (function () {
var a = 1;
var inner = function () {
return ++a;
};
return inner;
})();
console.log(outer());
console.log(outer());
outer = null; // outer 식별자의 inner 함수 참조를 끊음
→outer 함수를 null로 정의하며 outer 함수 내부에서 변수 a를 참조하던 inner 함수를 없애며 a를 참조하는 컨텍스트가 아무것도 없도록 만듦
-예시 2)
( function () {
var a = 0;
var intervalId = null;
var inner = function () {
if (++a >=10) {
clearInterval(intervalId);
inner = null; // inner 식별자의 함수 참조를 끊음
}
console.log(a);
};
intervalId = setInterval(inner, 1000);
})();
→함수 내부에서 변수를 참조하던 내부함수 inner 자체를 null로 정의하면 함수 내부변수 a룰 참조하는 것이 없도록 만듦
-예시 3)
( function () {
var count = 0;
var button = dovument.createElement('button');
button.innerText = 'click';
var clickHandler = function () {
concole.log(++count, 'times clicked');
if (count >= 10) {
button.removeEventListener('click', clickHandler);
clickHandler = null; // clickHandler 식별자의 함수 참주 끊음
}
};
button.addEventListener('click', clickHandler);
document.body.appendChild(button);
})();
→count 변수를 계속 참조하며 button에 eventListener로 주는 함수 clickHandler를 null로 정의하며 count를 참조할 컨텍스트를 없앰
'Front-end > Javascript' 카테고리의 다른 글
[코어 자바스크립트] 05. 클로저(3) (0) | 2022.12.21 |
---|---|
[코어 자바스크립트] 05. 클로저(2) (0) | 2022.12.21 |
[코어 자바스크립트] 04. 콜백 함수(2) (0) | 2022.12.14 |
[코어 자바스크립트] 04. 콜백 함수(1) (0) | 2022.12.12 |
[코어 자바스크립트] 03. this(2) (0) | 2022.12.08 |