Skip to content

Latest commit

 

History

History
210 lines (134 loc) · 6.4 KB

1-1.클로저의의미및원리이해.md

File metadata and controls

210 lines (134 loc) · 6.4 KB

back    |    write by Santos

1-1.클로저의 의미 및 원리 이해


* 이 글은 코어자바스크립트 책을 참고하여 기록하였습니다.

어떤 함수에서 선언한 변수를 참조하는 내부함수에서만 발생하는 현상.

1. 클로저란?

- 클로저란 어떤 함수 A에서 선언한 변수 a를 참조하는 내부함수 B를 "외부로 전달"할 경우 A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상.

- "외부로 전달"에 의미가 오직 return 만을 의미하는 것은 아님.

예시(외부 함수의 변수를 참조하는 내부함수 1번) 😎

var outer = function (){
    var a = 1; 
    var inner = function (){
        console.log(++a);            // 2 
    }

    inner();
};

outer();

* 예시를 통해 알아야 할 것들 🤔

  1. outer 함수에서 변수 a를 선언.

  2. outer의 내부 함수 inner 함수에서 a의 값을 1만큼 증가시킨 다음 출력.

  3. inner 함수 내부에서 a를 선언하지 않았기에, enviromentRecord 에서는 값을 찾지 못함.

  4. outerEnvironmentReference 에 지정된 상위 컨텍스트 outer의 LexicalEnvironment에 접근해서 a를 찾고 2를 출력.

  5. outer 함수의 실행컨텍스트가 종료 될 때, LexicalEnvironment에 저장된 식별자(a, inner)에 대한 참조를 지움.

  6. 참조하는 변수가 없는 값들은 가비지 컬랙터의 수집대상이 됨.


예시(외부 함수의 변수를 참조하는 내부함수 2번) 😎

var outer = function (){
    var a = 1; 
    var inner = function (){
        return ++a;
    }

    return inner();
};

var outer2 = outer();
console.log(outer2);            // 2

* 예시를 통해 알아야 할 것들 🤔

  1. outer 함수에서 변수 a를 선언.

  2. outer의 내부 함수 inner 함수에서 a의 값을 1만큼 증가시킨 다음 결과 값을 리턴.

  3. outer 함수에서 inner 함수를 실행한 결과를 리턴.

  4. outer 함수의 실행컨텍스트가 종료 될 때, LexicalEnvironment에 저장된 식별자(a, inner)에 대한 참조를 지움.

  5. 참조하는 변수가 없는 값들은 가비지 컬랙터의 수집대상이 됨.

- 결과적으로 예시 1번과 2번은 함수의 실행 컨텍스트가 종료될 때 LexicalEnvironment에 저장된 식별자에 대한 참조를 지우므로, 가비지 컬렉터의 수집대상이 됨.

예시(외부 함수의 변수를 참조하는 내부함수 3번) 😎

var outer = function (){
    var a = 1; 
    var inner = function (){
        return ++a;
    }

    return inner;
};

var outer2 = outer();
console.log(outer2());            // 2
console.log(outer2());            // 3

* 예시를 통해 알아야 할 것들 🤔

  1. outer 함수에서 변수 a를 선언.

  2. outer의 내부 함수 inner 함수에서 a의 값을 1만큼 증가시킨 다음 결과 값을 리턴.

  3. outer 함수에서 inner 함수 자제를 리턴.

  4. outer 함수의 실행컨텍스트가 종료 될 때, outer2 변수는 outer의 실행 결과인 inner 함수를 참조.

  5. outer2를 호출하면 반환된 함수인 inner 함수가 실행.

  6. inner 함수의 실행 컨텍스트의 enviromentRecord 에는 수집할 대상이 존재하지 않음.

  7. 이때 outerEnvironmentReference 에는 inner 함수가 선언된 위치의 LexicalEnvironment가 참조 복사됨. (중요)

  8. inner 함수는 outer 함수 내부에 선언되었으므로, outer 함수의 LexicalEnvironment가 담김.

  9. 스코프 체이닝에 의해 outer에 선언한 변수 a에 접근, 1을 증가 2를 반환, inner 함수의 실행 컨텍스트 종료.

  10. 다시 outer 함수 호출, 선언한 변수 a에 접근, 값 2에서 1 증가 3를 반환, inner 함수의 실행 컨텍스트 종료.

- inner 함수의 실행 시점에서 outer 함수는 이미 종료되었음에도 불구하고 outer 함수의 LexicalEnvironment의 접근할 수 있는 이유는 가비지 컬렉터의 동작 방식 때문.(중요)

- 가비지 컬렉터는 어떤 값을 참조하는 변수가 하나라도 있다면 수집대상에 포함시키지 않음.

- outer 함수가 종료, 실행 컨텍스트가 종료되었음에도 내부 함수인 inner 함수가 반환되어 outer2 변수에 담기면서, 함수를 호출 할 가능성이 존재하므로, 가비지 컬렉터의 대상이 되지 않아 a라는 변수에 접근할 수 있게 됨.

- 결론적으로, 지역변수를 참조하는 내부함수가 외부로 전달 된 경우가 가비지 컬렉터의 수집 대상에서 제외된다고 할 수 있음.

- 클로저는 "외부 함수의 LexicalEnvironment가 가비지 컬렉팅되지 않는 현상" 자체.

예시(return 없는 클로저의 발생) 😎

# 1 setInterval / setTimeout 함수
(function (){
    var a = 0;
    var intervalId = null;
    var inner = function (){
        if(++a >= 10){
            clearInterval(intervalId);
        }
        console.log(a);             // (1초에 한번 씩) 1 2 3 4 5 6 7 8 9 10
    };
    intervalId = setInterval(inner, 1000);
})();

#2 
(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);
})();

* 예시를 통해 알아야 할 것들 🤔

  1. #1은 외부객체인 window의 메서드 setInterval에 전달할 콜백 함수 내부에서 지역변수를 참조.

  2. #2는 외부객체인 DOM의 메서드 addEventListenerd에 등록 할 콜백 함수 내부에서 지역변수를 참조.

- 결과적으로 #1, #2번 모두 지역변수를 참조하는 내부함수를 외부에 전달, 그렇기에 클로저.

읽어 주셔서 감사합니다.


<참고자료>

[책] #코어자바스트립트 - 정재남 지음 -

<클로저> 클로저의 의미 및 원리 이해 end