\(@^0^@)/

[BOOK] 코어 자바스크립트, 클로저 1 본문

BOOKS/코어 자바스크립트

[BOOK] 코어 자바스크립트, 클로저 1

minjuuu 2022. 3. 29. 16:10
728x90

오늘 읽은 범위 : (클로저) p115 ~ p.124


< 책에서 기억하고 싶은 내용 >

클로저

클로저의 의미

  • 자신을 내포하는 함수의 콘텍스트에 접근할 수 있는 함수
  • 함수를 선언할 때 만들어지는 유효 범위가 사라진 후에도 호출할 수 있는 함수
  • 로컬 변수를 참조하고 있는 함수 내의 함수
  • 어떤 함수에서 선언한 변수를 참조하는 내부 함수에서만 발생하는 현상

 

클로저의 원리

  • 일반적인 외부 함수의 변수를 참조하는 내부 함수
var outer = function () {
	var a = 1;
    var inner = function () {
    	console.log(++a);
    };
    inner();
};
outer();

 

  • 클로저 발생하는 외부 함수의 변수를 참조하는 내부 함수
var outer = function () {
	var a = 1;
    var inner = function () {
    	return ++a;
    };
    return inner;
};
var outer2 = outer();
console.log(outer2());      // 2
console.log(outer2());      // 3
  • 6번째 줄에서 inner 함수의 실행 결과가 아닌 inner 함수 자체를 반환했음.
  • 스코프 체이닝에 따라 outer에서 선언한 변수 a에 접근해서 1만큼 증가시킨 후 그 값인 2를 반환하고, inner함수의 실행 콘텍스트가 종료된다.
  • 10번째 줄에서 다시 outer2를 호출하면 같은 방식으로 a의 값을 2에서 3으로 1 증가시킨 후 3을 반환함.
  • inner함수의 실행 시점에는 outer함수는 이미 실행이 종료된 상태인데 outer함수의 LexicalEnviornment에 접근할 수 있는 이유는 가비지 컬렉터(GC)의 동작 방식 때문.
    • 가비지 컬렉터는 어떤 값을 참조하는 변수가 하나라도 있다면 그 값은 수집 대상에 포함시키지 않는다.
    • 따라서, 외부 함수인 outer의 실행이 종료되더라도 내부 함수인 inner함수는 언젠가 outer2를 실행함으로써 호출될 가능성이 열린 것.
  • 클로저 : 어떤 함수 A에서 선언한 변수 a를 참조하는 내부 함수 B를 외부로 전달할 경우 A의 실행 콘텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상.

  • 주의할 점
    • '외부로 전달'이 곧 return만을 의미하는 것은 아니다.
    • return 없이도 클로저가 발생하는 다양한 경우 : setInterval/setTimeout/eventListener

// (1) setInterval/setTimeout

(function () {
	var a = 0;
    var intervalId = null;
    var inner = function () {
    	if (++a >= 10) {
        	clearInterval (intervalId);
        }
        console.log(a);
    };
    intervalId = setInterval(inner, 1000);
})();
// (2) eventListener

(function () {
	var count = 0;
    var button = document.vreateElement('button');
    button.innerText = 'click';
    button.addEventListener('click', function () {
    	console.log(++count, 'times clicked');
    });
    document.body.appendChild(button);
})();

 

클로저와 메모리 관리

  • 메모리 누수의 위험을 이유로 클로저 사용을 조심해야 한다거나 지양해야 한다고 주장하는 사람들도 많지만,
    메모리 소모는 클로저의 본질적인 특성일 뿐임. 이러한 특성을 정확히 이해하고 잘 활용하도록 노력해야 함.
  • 개발자가 의도적으로 참조 카운트를 0이 되지 않게 설계한 경우는 '누수'라고 할 수 없다.
    의도대로 설계한 '메모리 소모'에 대한 관리법만 잘 파악해서 적용하는 것으로 충분
  • 관리 방법
    • 클로저는 어떤 필요에 의해 의도적으로 함수의 지역변수 메모리를 소모하도록 함으로써 발생한다.
      그렇다면 그 필요성이 사라진 시점에 더는 메모리를 소모하지 않게 해 주면 되는 것.
    • 참조 카운트를 0으로 만들면 언젠가 GC가 수거해갈 것이고, 이때 소모됐던 메모리가 회수된다.
    • 참조 카운트를 0으로 만드는 방법 : 식별자에 참조형이 아닌 기본형 데이터(보통 null이나 undefined)를 할당.
// (1) return에 의한 클로저의 메모리 해제
var outer = function () {
	var a = 1;
    var inner = function () {
    	return ++a;
    };
    return inner;
};
console.log(outer());      
console.log(outer());
outer = null;                     // outer 식별자의 inner 함수 참조를 끊음.
// (2) setInterval에 의한 클로저의 메모리 해제

(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);
})();
// (2) eventListener에 의한 클로저의 메모리 해제

(function () {
    var count = 0;
    var button = document.vreateElement('button');
    button.innerText = 'click';
    
    var clickHandler = function () {
    	console.log(++count, 'times clicked');
        if (count >= 10) {
        	button.removeEventListener('click', clickHandler);
            clickHandler = null;                         // clickHandler 식별자의 함수 참조를 끊음
        }
};

< 정리 >

  • 클로저란 어떤 함수 A에서 선언한 변수 a를 참조하는 내부 함수 B를 외부로 전달할 경우 A의 실행 콘텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상.
  • 메모리 소모는 클로저의 본질적인 특성일 뿐임. 이러한 특성을 정확히 이해하고 잘 활용하도록 노력해야 함.
  • 참조 카운트를 0으로 만들면 언젠가 GC가 수거해갈 것이고, 이때 소모됐던 메모리가 회수된다.
  • 참조 카운트를 0으로 만드는 방법 : 식별자에 참조형이 아닌 기본형 데이터(보통 null이나 undefined)를 할당.

[ 출처 : 코어 자바스크립트 ]
[ 참고 :  https://www.youtube.com/watch?v=tpl2oXQkGZs&list=PLZKTXPmaJk8JZ2NAC538UzhY_UNqMdZB4&index=11 ]
[ 참고 : https://www.youtube.com/watch?v=Um-CJHNc5Pw

728x90