본문 바로가기

프로그래밍 언어/Javascript

Javascript) 클로저(Closure)란?

이 글은 아래 링크의 글을 공부하면서 정리한 글입니다. 

 

JavaScript 클로저(Closure)

클로저란?MDN에서는 클로저를 다음과 같이 정의하고 있다. 클로저는 독립적인 (자유) 변수를 가리키는 함수이다. 또는, 클로저 안에 정의된 함수는 만들어진 환경을 ‘기억한다’. 흔히 함수 내

hyunseob.github.io


클로저(Closure)란?

  • 독립적인 자유 변수 
  • 함수 내에서 함수를 정의하고 사용하는 것 
  • 내부 함수가 상위 스코프의 식별자를 참고하고, 그 상위 스코프를 외부에서 사용했을 때 내부 함수에 의해 참조되고 있는 상위 스코프의 식별자를 수정할 수 없는 형태 
  • 외부 함수는 내부 함수의 지역 변수에 접근할 수 없지만 내부 함수는 외부 함수의 변수에 접근할 수 있다 
  • 클로저는 각자의 환경을 가지고 있고 기억한다 그래서 메모리를 차지하고 있기 때문에 클로저 사용이 끝나면 참조를 제거하는 것이 좋다 
  • 예시
    function getClosure() {
      var text = 'variable 1';
      return function() {
        return text;
      };
    }
    
    var closure = getClosure();
    console.log(closure()); // 'variable 1'​
    getClosure는 해당 함수 내에서 선언된 text라는 변수를 반환하는 함수를 반환하는 함수다 (ㅋㅋ) 이렇게 getClosure에서 선언된 text는 함수 실행이 끝났어도 사라지지 않기 때문에 외부에서 여전히 text 함수를 사용할 수 있다. 
  • 예시2
    var base = 'Hello, ';
    function sayHelloTo(name) {
      var text = base + name;
      return function() {
        console.log(text);
      };
    }
    
    var hello1 = sayHelloTo('승민');
    var hello2 = sayHelloTo('현섭');
    var hello3 = sayHelloTo('유근');
    hello1(); // 'Hello, 승민'
    hello2(); // 'Hello, 현섭'
    hello3(); // 'Hello, 유근'
    
    // 클로저 할당한 메모리 release
    hello1 = null;
    hello2 = null;
    hello3 = null;
    sayHelloTo라는 함수는 name이라는 인자와 함수밖에서 선언된 base라는 함수를 합친 text라는 변수를 함수내에서 선언하고 이걸 콘솔에 출력하는 함수를 반환하는 함수다. 그리고 이 함수를 세번 실행시키고 각 실행마다 반환된 함수가 별도의 변수에 저장된다. 그리고 반환된 함수가 저장된 변수를 실행시켜보면 여전히 각 함수의 환경 즉, text를 그대로 유지하고 있다. 

클로저를 통한 은닉화 

  • 은닉화 : 외부 객체로부터 속성 (데이터, 멤버변수) 등의 값을 감추는 특성을 말한다. 이는 외부로부터 데이터를 보호하기 위한 특성으로 클래스 내 변수를 private 으로 선언함으로써 은닉화가 가능하다. 
  • 예시
    function hello(name) {
      var _name = name;
      return function() {
        console.log('Hello, ' + _name);
      };
    }
    
    var hello1 = hello('승민');
    var hello2 = hello('현섭');
    var hello3 = hello('유근');
    
    hello1(); // 'Hello, 승민'
    hello2(); // 'Hello, 현섭'
    hello3(); // 'Hello, 유근'
    여기서 Hello 객제들은 전부 _name 이라는 변수를 가진다. 이렇게 클로저를 사용하면 _name이라는 프라이빗 변수를 외부에서 접근할 수 없다. 인터페이스를 만들어서 접근하는 방법뿐! 

 

클로저의 원리 

클로저를 사용하면 함수가 종료된 시점에도 내부에 저장된 함수에 접근이 가능하다. 이는 가비지 컬렉터의 동작 방식 때문이다. 가비지 컬렉터는 어떤 값을 참조하는 변수가 하나도 없을 경우에만 수집하는데 클로저는 함수 내부에 함수를 선언하게 되면 그 외부 함수에 의해 내부 함수가 항상 참조된 상태기 때문에 함수가 끝난 시점에도 외부 함수는 내부 함수를 참조하고 있다. 그래서 항상 내부 함수는 참조 대상이 되어 있는 상태기 때문에 자비지 컬랙터에 의해 컬렉팅되지 않는다. 즉, 어떤 함수의 지역 변수를 참조하는 내부 함수가 외부로 반환되는 경우 클로저가 발생하는 것이고 이 경우 가비지 컬렉터의 수집 대상에서 제외되기 때문에 외부 함수가 종료된 시점에도 내부 함수에서 사용된 지역 변수에 값이 여전히 저장되어 있는 것이다! 

더보기

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

출처: https://codingsalon.tistory.com/24 [코딩쌀롱]

function outer(){
	var local_variable = "outer_local_variable";
    return function (){
    	console.log(local_variable)
    }
}

outer(); // outer_local_variable

 

클로저 메모리 관리

클로저는 의도적으로 어떤 함수내 지역 변수가 계속해서 메모리를 차지하고 있도하여 발생되는 현상이다. 즉, 함수가 종료된 시점에도 그 함수의 지역 변수가 메모리를 계속 차지하고 있는 것이기 때문에 사용 후엔 반드시 해당 메모리 참조를 지워 메모리를 비워줘야 한다. 예를 들어 위 예시에서 outer=null이라고 해주면 이제 내부에 local_variable을 참조한고 있는 것이 없기 때문에 local_variable은 가비지 컬렉터 대상이 되어 메모리가 회수된다. 

 

클로저의 장점

  • 데이터 보존
    클로저 함수는 외부 함수 실행이 끝나고 외부 함수 내 지역변수를 사용할 수 있다. 그래서 스코프 안에 가둔 특정 데이터를 보존하여 계속 사용할 수 있게 한다
  • 캡슐화
    객체로 여러개 함수를 만들어 리턴하면 그 함수들이 기억되어 계속 사용할 수 있게 된다. 이렇게 함수를 반환하는 객체들을 만들어 그 함수 내부의 데이터에 접근을 제한할 수 있다 
  • 모듈화
    클로저로 데이터와 메소드를 항상 묶어둘 수 있다 

 


References