Jiyong's STUDY

클로저(Closure)란? 본문

Web/자바스크립트

클로저(Closure)란?

Kingjiyong 2021. 6. 24. 04:08

클로저는 함수와 함수가 선언된 렉시컬 환경과의 조합이라고 한다.

클로저를 알기 위해서는 렉시컬 환경과 실행 컨텍스트에 대해서 알고 있어야만 비로소 알 수 있는 것이라고 말할 수 있다.

 

function init() {
    var name = "Mozilla"; // name is a local variable created by init
    function displayName() { // displayName() is the inner function, a closure
        alert (name); // displayName() uses variable declared in the parent function    
    }
    displayName();    
}
init(); // Mozilla에서 Closure를 설명하며 든 예시 코드

init() 안에 있는 displayName()는 내부의 alert를 실행하기 위해서

자신의 컨텍스트 밖인 init()의 컨텍스트에서 name을 참조하고 있다. 

 

Mozilla는 이를 통해 렉시컬 환경과 실행 컨텍스트를 이해시키고자 한다.

 

이제, 클로저를 보자.

 

function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();

결과적으로 myFunc()가 가지고 있는 값은 makeFunc() 내부에 존재하는 함수인 displayName()이다.

일반적으로는 외부 함수가 종료되면 지역 스코프의 값들은 사라진다. 하지만 자바스크립트는 사라지지 않는다.

이것이 함수가 선언된 렉시컬 환경과의 조합이고 클로저다.

 

클로저를 사용하며 실수를 할 수 있다. 클로저는 정확히 말하면 참조하는 것을 말한다.

무엇을 참조하냐? 자신이 생성될 때의 렉시컬 환경의 변수를 참조한다.

 

예시는 다음과 같다.

 

<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>
function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp();

 

이 경우, setupHelp()는 정확하게 돌지 않는다.

이 코드에는 하나의 클로저가 존재한다. showHelp()가 호출되는 부분에 있다.

 

다른 이야기로 넘어가서 왜 이게 클로저인가? showHelp()는 왜 클로저가 되는가?

showHelp()는 내부에서 선언된 변수를 사용하고 있지 않다. 정확히 말하면 매개 변수를 통하여 외부에서 받아온다.
그리고 showHelp()는 setupHelp() 안에서 실행되며 그 함수 내부의 변수를 참조하여 사용한다.
이 경우에 showHelp()는 외부의 변수를 사용한 것이 되고 그 시점을 기억하게 된다.
즉 외부의 변수를 기억하여 사용하게 되는 것이고 비로소 클로저라고 부를 수 있는 상황이 된 것이다.

 

클로저는 값을 받아오는 것이 아니라, 함수가 선언된 렉시컬 환경과 조합되어 "참조"하는 것이다.

즉 특정 요소의 onfocus 속성에 함수를 정의할 때 item.help는 하나의 렉시컬 환경만 참조하게 된다.

외부 함수인 showHelp()가 item.help를 사용하기 위하여 생성된 그 렉시컬 환경을 계속 참조하게 된다는 이야기다.

 

그래서, item이 계속 변경되며 마지막으로 item.help의 상태는 age의 help가 된다. 그래서 어떤 문자열에 focus가 되어도 age에 대한 설명만 제공할 뿐 제대로 동작하지 못한다.

이 경우에 제대로 동작시키기 위해서는 새로운 클로저를 생성해야 한다. 3개의 클로저가 각 케이스에 따라 올바른 설명을 제공시키기 위함이다.

 

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function makeHelpCallback(help) {
  return function() {
    showHelp(help);
  };
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
  }
}

setupHelp();

makeHelpCallback()이 클로저를 생성하여 제대로 동작하게 된다.

물론 새로운 함수를 생성하지 않고 var item을 let item으로 변경하면 블록 범위 스코프를 남기기에 추가적인 클로저를 생성하지 않아도 작동한다.

'Web > 자바스크립트' 카테고리의 다른 글

Javascript의 call, apply, bind  (0) 2021.06.29
Javascript의 this와 call  (0) 2021.06.28
Javascript의 Hoisting과 Scope  (0) 2021.06.28
Javascript의 실행 컨텍스트  (0) 2021.05.26
Javascript의 함수  (0) 2021.05.26