Dev/JavaScript

[JavaScript] Closure(클로저)

Moss 2014. 4. 5. 23:17

 Javascript의 모듈 패턴(Module Pattern)을 이해하기 위해서 선행학습이 필요한 Closure 개념을 나름대로 정리해 보았습니다.

 우선 Closure를 이해하기 위해서는 아래 내용들의 사전 학습이 필요합니다. 아래 글은 기본적인 개념을 이해하고 있다는 가정하에 작성되었습니다.

  • 유효범위(Scope)
  • 가비지 컬렉셔(Gabage Collection, 이하 GC)
  • 이름없는 함수(Anonymous function)
  • Javascript에서 function은 object로 처리됩니다. (이는 GC의 정리 대상 여부와 관련이 됩니다.)

정 의

 클로저(Clousure)는 자신의 유효범위(Scope) 외의 변수들을 참조할 수 있는 정보를 가지는 참조 환경(referencing environment)을 가지는 함수또는 함수의 참조이다.

Wikipedia in Closure (computer programming)

위의 정의는 일반적인 프로그래밍에서의 클로저(Closure)정의이기에 조금 명확하지 않은데, Javascript에서는 아래와 같이 해석할 수 있습니다.

 자바스크립트에서 클로저는 함수의 생명주기는 끝이났지만 함수내의 변수를 내부함수가 참조하고 있기 때문에 유지되어 접근할수 있는 함수이다.

Outsider in 자바스크립트 클로저(Closure)에 대해서...

간단한 예제

클로저(Closure)의 간단한 예제를 살펴 봅시다. 호출 할 때마다 1부터 시작하는 연속적인 수를 반환하는 함수입니다.

var sequencer = function() {
  var count = 0;
  return function() {
      return ++count; 
  }
};

var seq = sequencer();

seq(); // 1
seq(); // 2
seq(); // 3

 일반적인 언어에서 보기 힘든 문법을 확인 할 수 있습니다. function을 호출했더니 function을 반환합니다. 또한 반환된 함수를 호출 할 때마다, 자신의 유효범위(scope)에 해당하지 않는 count를 지속적으로 사용해서 count에 1을 더하고 이 값을 반환하는 것을 확인할 수 있습니다.

 sequencer() 가 호출되는 시점에 count의 생명주기는 끝나기 때문에 count는 GC에 의해서 정리가 되어야 하는데 어떻게 count가 계속 유효할가요?

 javascript의 function이 object라는 사실을 생각하면 좀 더 쉽게 생각할 수 있습니다. 왜냐하면 sequencer()가 호출 될 때 function(object)[seq()]이 반환되는데, 이 function(object)이 count 변수를 참조하고 있기때문에 비록 count 변수를 정의한 function[sequencer()]의 생명주기가 끝났지만 count의 참조가 유효하기 때문에 GC에 의해서 정리가 되지 않기 때문입니다. 이 말은 다르게 생각하면 메모리 릭이 발생할 수 있는 포인트라서 조심해야 하는 부분이 됩니다.

조금은 복잡한 예제

위의 예제로 이해가 되지 않는다면 아래 코드 및 결과를 바탕으로 이해를 해보시면 될 것 같습니다.

var sequencer = function(base) {
  var count = base;
  return function() {
      return ++count; 
  }
};

var base_0_seq = sequencer(0);
var base_5_seq = sequencer(5);

base_0_seq(); // 1
base_0_seq(); // 2
base_5_seq(); // 6
base_5_seq(); // 7
base_0_seq(); // 3

성 능

성능과 관련된 글을 MDN에 잘 정리되어 있어서 참조를 했습니다.

클로져가 필요하지 않은 작업인데도 함수안에 함수를 만드는 것은 스크립트 처리 속도와 메모리 사용량 모두에서 현명한 선택이 아니다.

예를들어 새로운 오브젝트나 클래스를 만들 때 오브젝트 생성자에 메쏘드를 정의하는 것 보다 오브젝트의 프로토타입에 정의하는것이 좋다. 오브젝트 생성자에 정의하게 되면 생성자가 불릴때마다 메쏘드가 새로 할당되기 때문이다.

jaemin_jo in MDN 클로져(Closure)

나쁜 예는 method 정의가 function object 생성때 마다 발생하지만, 아래 좋은 예에서는 상속된 속성은 모든 object에서 사용할 수 있고 method정의가 function object 새성 때 마다 일어나지 않습니다.

나쁜 예

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function() {
    return this.name;
  };

  this.getMessage = function() {
    return this.message;
  };
}

좋은 예1

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype = {
  getName: function() {
    return this.name;
  },
  getMessage: function() {
    return this.message;
  }
};

좋은 예2

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function() {
  return this.name;
};
MyObject.prototype.getMessage = function() {
  return this.message;
};

결 론

클로저(Closure)는 언듯보면 유효범위(Scope)에 맞지 않는 동작을 보이는 듯 하나, 가비지 컬렉션(Gabage Collection)과 Javascript에서 function은 object라는 점을 고려하면 자연스러운 동작인 것을 확인 할 수 있습니다.

개인적으로 나중에 제가 읽고 다시 이해할 정도로 클로저(Closure)를 정리하다보니 내용이 많이 부족하네요. 혹시 이해가 안되시면 댓글을 남겨주시면 내용을 추가하거나 아는한도 내에서 답변을 해드리겠습니다. (Javascript는 초보라서 사실 위에 내용도 정확한지 잘 모르겠네요;;;) 또한 아래 제가 읽은 좋은 글들을 링크해 두었으니 참조하시면 될 것 같습니다.

참고자료

MDN 클로져(Closures)
HTML5JS에서 발표한 Closure와 Scope Chain
Closure in javascript (자바스크립트에서 클로져는 무엇인가?)
자바스크립트 클로저(Closure)에 대해서...
자바스크립트의 클로저 (JavaScript's Closure)
[번역] Explaining Javascript Scope and Closure