클로저 이해하기 - Lee-hyuna/33-js-concepts-kr GitHub Wiki

원본출처: https://codeburst.io/understanding-javascript-closures-da6aab330302

자바스크립트를 처음 배울 때면, 클로저와 같은 단어는 자체만 봐도 어렵기만 하다, 자바스크립트로 클로저가 뭐야? 라고 생가가된다면 아마도 누구든 코드를 짜면서 클로저를 사용해보았을 것이다.

이는 JavaScript 언어가 클로저에 의존하여 간결하고 효율적인 프로그램을 만들기 때문이다.

클로저란?

다른 함수 내에서 함수를 선언하는 경우, 스코프 체인이을 만든다. 외부 부모에서는 자식(ren)의 변수를 볼 수 없지만 자식은 부모의 변수를 볼 수 있다. 마트료쉬카 인형을 생각해보자. 이제 인형들이 생명을 부여받았다고 상상해 보자. 바깥 인형은 내면의 인형의 소름끼치는 내장을 볼 수 없을 것이다. 안쪽 인형은 모든 것을 볼 수 있다.

// We have two functions. One is named outie and the other is named closure *wink* *wink*
  
  //this is closure's global scope
function outie(){
  // this is closure's first and only outer scope
  function closure(){
   // this is closure's local scope
  }
}

내부 기능에는 세 개의 스코프 체인이 있다.

  1. 첫 번째 체인을 통해 내부 기능이 자체 변수에 액세스할 수 있다.

  2. 두 번째 기능을 사용하면 외부 기능의 변수와 파라미터에 액세스할 수 있다.

  3. 세번째는 글로벌 변수에 접근할 수 있게 해준다. 기능을 추가할 때마다 체인에 새로운 링크를 추가하고 있다.

다른 기능 내에서 함수를 정의함으로써 클로저를 만든다. 또한 클로저 자체는 외부 기능의 역할을 수행할 수 있다. 여러 개의 클로저에 대한 다소 실용적인 예를 들어 보자.

function sayMyName(firstName, middleName, lastName){
  var praiseList = ["beautiful", "awesome", "splendid", "fantastic", "jaw-dropping"]; 
  //this first closure called combineName() has access to the parameters
  //of the outer function and its variable
  function combineName(){ 
    var min = 0;
    var max = praiseList.length - 1;  
     //this second getRandomInt() closure has access to the variables in combineName()  
    function getRandomInt(){    
       return Math.floor(Math.random() * (max - min) + min);      
    }  
    return "Your" + " " + praiseList[getRandomInt()] + " name is " + firstName + " " + middleName + " " + lastName;
  } 
  return combineName();
}
 sayMyName("Beyonce", "Giselle", "Knowles-Carter"); // Your beautiful/awesome/splendid... name is Beyonce Giselle Knowles-Carter 

이 예에서는 두 개의 클로저가 있다. combleName()sayMyName()의 외부 기능에 대한 클로저다. getRandomInt()combineName()sayMyName()을 결합하는 클로저 역할을 한다.

즉, combleName()에는 3개의 스코프 체인이 있고 getRandomInt()에는 4개의 스코프 체인이 있다. 닫기를 생성할 때마다 하나의 링크가 스코프 체인에 추가된다. 따라서 getRandomInt()complineName()의 변수에 액세스하여 sayMyName()이라고 말할 수 있다.

Side Note 이 예에서 볼 수 있듯이, sayMyName에서 매개 변수 "borrow"를 사용할 수 있었다. 이 모든 것은 ahem이라는 트릭 다운 효과 범위 체인이 가지고 있는 덕분이었다. 그러나 한 가지 유의해야 할 점은 외부 함수의 인수 개체를 클로저 내에서 호출할 수 없다는 것입니다.

Closures In Depth

클로저란 어떤 것인지 간략하게 정리해보자. 이제 몇 가지 사용 사례를 살펴보고 클로저의 가장 깊은 부분을 살펴보도록 하겠다.

클로저는 기본적으로 워킹데드이다. 함수가 반환되면 해당 함수 내의 모든 변수는 존재하지 않아야 한다. 하지만 클로저는 여전히 범위 체인의 변수에 액세스할 수 있다.

function init(args){
  var firstName = args.firstName;
  var lastName = args.lastName;
    
  function gerunding(action){
    return firstName + " " + lastName + " " + "is" + " " + action;
  }
  return gerunding;
 } 
  
var data = {firstName: "George", lastName: "Rooney" }; 
var zombieOne = init(data); //when we initialize the outer function, it returns undefined. The outer function is dead, but...
/*
 20 lines of code later...
*/
zombieOne("walking"); // George Rooney is walking

여전히 "zombie"의 이름에 접근할 수 있는 이유는 어떤 공상 과학 마술 때문이 아니라 클로저가 값을 저장하지 않기 때문이다. 값은 정적이다. 대신 스코프 체인에 값에 대한 참조를 저장한다.

놀라운 점은 클로저를 반환하기 전에 스코프 체인에 있는 변수의 값을 변경하면 클로저 내에서 사용되는 값과 동일한 값이 업데이트된다는 것이다.

이 현상을 state라고 한다.

앱의 수명 동안 내부 데이터가 상주하는 기능을 만들 수 있기를 원하기 때문에 사용자가 데이터를 입력할 때 해당 데이터를 업데이트해야 한다.

위의 코드를 수정하여 클로저를 사용하여 모듈 패턴을 만드는 예를 만들어 보자.

var zombieOne = (function(){
    //private variables
      var firstName = "";
      var lastName = "";
    //private functions
      function init(data){
         firstName = data.firstName;
         lastName = data.lastName;
      }
      
      function combineName(){
         return firstName + " " + lastName;
      }
  
      function gerunding(action){
        return firstName + " " + lastName + " " + "is" + " " + action;
      }
      
    //public functions
      return {    
        getName: function(){
          return combineName();
        },
        setName: function(data){
          return init(data);
        },
        setAction: function(action){
          return gerunding(action);
        }
     };     
 })();
    
var data = {firstName: "George", lastName: "Rooney" };
  
zombieOne.setName(data);
zombieOne.getName(); // "George Rooney"
zombieOne.setAction("walking");// "George Rooney is walking"

첫 번째 함수를 괄호 안에 포함시킴으로써 익명의 클로저가 된다

(function(){…})();

익명 클로저 혹은 즉시실행함수는 즉시 실행되며 상태를 유지하고 개인 정보를 관리할 수 있다.

C++ 또는 Ruby와 같은 다른 객체 지향 언어와 달리, JavaScript는 아직 강력한 룰이 없다. ES6의 새로운 클래스 키워드를 맛본 후 사설 구문을 찾는 JavaScript 개발자가 있다. 현재로서는 비공개/공개 데이터를 코드화하기 위해 클로저를 해야 한다.

익명 클로저 내의 모든 함수 또는 변수는 비공개이다. return을 한 후 인자만 바꿀 수 있을 뿐 모듈 외부에서 내부함수에 대한 값을 바꿀 수 없다.

대부분의 개인 정보가 익명의 클로저로 인해 효과적으로 차단된다.

결론

클로저(closures)는 JavaScript 기능의 유연성을 보여주는 대표적인 예다. 몇 가지 예에서 기능을 변수로 설정해 놓았다. 익명 함수라고 불리는 반환된 함수는 함수를 키로 사용했다. 함수를 스스로 불러온 것은 말할 것도 없다.

이 모든 기능적이고 곡예적인 속임수들은 모두 그들에게 적용되는 용어들을 가지고 있다. 클로저는 다른 기능 내에서 정의되는 기능이라는 점을 기억하자!