Front-End/JavaScript

[JavaScript] Closure (클로저)

Voyage_dev 2022. 3. 9. 18:36

Closure (클로저)는 함수와 그 함수가 선언됐을 때의 렉시컬 환경 (Lexical Environment)과의 조합이다. 클로저를 이해하려면 렉시컬 환경에 대해서 먼저 이해해야 한다

 

참고 : https://voyage-dev.tistory.com/33

 

[JavaScript] 스코프 (Scope)

Scope란 스코프란 식별자에 대한 유효범위 즉, 모든 식별자 (변수 이름, 함수 이름, 클래스 이름 등)는 자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효 범위가 결정된

voyage-dev.tistory.com

자세한 내용은 위에 링크를 클릭하고 간단하게 렉시컬 환경이란 함수를 어디서 호출하는지가 아니라 어디에 선언하였는지에 따라 결정되는 것이라고 생각하면 된다

즉, 함수를 어디서 선언하였는지에 따라 상위 스코프를 결정한다는 뜻이며, 함수의 선언에 따라 결정된다

클로저

함수와 함수가 선언된 어휘적 (lexical) 환경의 조합이다 즉, 클로저는 함수를 지칭하고 또 그 함수가 선언된 환경과의 관계라는 개념이 합쳐진 것이다

 

클로저의 핵심은 스코프를 이용해서, 변수의 접근 범위를 닫는 것에 있다. 외부함수 스코프에서 내부함수 스코프로 접근은 불가능 하지만 내부함수에서는 외부함수 스코프에서 선언된 변수에 접근 가능하다. 따라서, 내부 함수는 외부함수에 선언된 변수에 접근 가능하다

함수가 호출되는 환경과 별개로, 기본에 선언되어 있던 환경 (렉시컬)을 기준으로 변수를 조회한다. 외부함수의 실행이 종료된 후에도, 클로저 함수는 외부함수의 스코프, 즉, 함수가 선언된 어휘적 환경에 접근할 수 있다. 외부 함수 스코프가 내부 함수에 의해 언제든지 참조될 수 있지만 클로저를 남발할 경우 퍼포먼스 저하가 발생할 수도 있다

클로저 간단한 예제

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

    var myFunc = makeFunc();
    //myFunc변수에 displayName을 리턴함
    //유효범위의 어휘적 환경을 유지
    myFunc();
    //리턴된 displayName 함수를 실행(name 변수에 접근)

위 예제를 보면 displayName() 함수가 실행되기 전에 외부함수인 makeFunc()로부터 리턴되어 myFunc 변수에 저장된다는 것이다

자바스크립트는 함수를 리턴하고, 리턴하는 함수가 클로저를 형성하기 때문이다. 클로저는 함수와 함수가 선언된 어휘적 환경의 조합이기 때문에 클로저가 생성된 시점의 유효 점위 내에 있는 모든 지역 변수로 구성된다. 예시의 경우, myFunc은 makeFunc이 실행 될 때 생성된 displayName 함수의 인스턴스에 대한 참조다. displayName의 인스턴스는 변수 name 이 있는 어휘적 환경에 대한 참조를 유지하므로 myfunc가 호출될 때 변수 name은 사용할 수 있는 상태로 남게 되고 “Voyage”가 alert에 전달된다

그 이유는 무엇 때문인가?

클로저는 반환된 내부 함수가 자신이 선언되었을 때의 환경 (Lexical Environment)인 스코프를 기억하여, 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있다

 

더 간단히 말하면, 클로저는 자신이 생성될 때의 환경(렉시컬)을 기억하는 함수라고 볼 수 있다

클로저 함수의 장점 (팔요한 이유)

  1. 데이터를 보존할 수 있다
  • 클로저 함수는 외부 함수의 실행이 끝나더라도 외부 함수 내 변수를 사용할 수 있기 때문에 클로저는 이처럼 특정 데이터를 스코프 안에 가두어 둔 채로 계속 사용할 수 있게 하는 폐쇄성을 갖는다

   2. 정보의 접근 제한 (캡슐화)

  • 클로저 모듈 패턴을 사용해 객체에 담아 여러 개의 함수를 리턴하도록 만든다. 이러한 정보의 접근을 제한하는 것을 캡슐화라고 한다

   3. 모듈화에 유리

  • 클로저 함수를 각각의 변수에 할당하면 각자 독립적으로 값을 사용하고 보존할 수 있다. 이와 같이 함수의 재사용성을 극대화 함수 하나를 독립적인 부품의 형태로 분리하는 것을 모듈화라고 한다. 클로저를 통해 데이터와 메소드를 묶어다닐 수 있기에 클로저는 모듈화에 유리하다

   4. 전역변수를 줄일 수 있다

const btn = document.querySelector('button')

btn.addEventListener('click',handleClick)

let count = 0
function handleCilck(){
  count++
  return count
}

위와 같은 경우에 count를 전역변수로 사용해줘야 count의 증가를 해줄 수 있다 이럴경우 클로저를 사용하면외부함수 handleClick의 렉시컬 환경을 참조하는 함수를 btn의 콜백함수로 이용해 전역객체 없이 구현할 수 있다

  • 클로저는 언제나 함수를 리턴하는 함수가 아니라 내부함수가 상위 스코프의 식별자를 참조하고 있고 그 상위 스코프에 바깥에서 사용했을 때 그 상위 스코프의 식별자를 수정할 수 없는 형태이다
(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)
})();
// 별도의 외부객체인 DOM의 메서드(addEventListener)에 등록할 handler함수 내부에서 지역변수를 참조!

 

지역변수를 참조하는 내부함수를 외부에 전달했기에 클로저는 맞지만, 외부로 전달이 항상 return을 의미하는 것은 아니라는 정도로 생각하면 될 것 같다

결론

클로저 (Closure) 함수는 외부 함수 밖에서 내부 함수가 호출되더라도 외부 함수의 지역 변수에 접근할 수 있다 그 이유는 자신이 생성될 때의 환경 즉, Lexical Environment을 기억하고 있기 때문이다

 

출처 : 아래의 사이트들을 보면서 큰 공부 하였습니다

https://ljtaek2.tistory.com/147?category=863722

https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures

https://hanamon.kr/javascript-클로저/

https://velog.io/@proshy/JS클로져closure와-클로져의-사용-예제

코어자바스크립트 - 클로저