출처 : MDN

클로저를 이해하려면, 어휘적 범위 지정 Lexical scoping을 이해해야 한다

어휘적 범위 지정(Lexical scoping)

 function init() {
      var name = "Mozilla"; // name은 init에 의해 생성된 지역 변수이다.
      function displayName() { // displayName() 은 내부 함수이며, 클로저다.
        alert(name); // 부모 함수에서 선언된 변수를 사용한다.
      }
      displayName();
 }
 init(); 

init()은 지역변수 name과 내부 함수 displayName()을 생성한다
내부 함수 displayName()은 외부 함수의 변수 name에 접근할 수 있다
만약 displayName 내부에도 name이라는 변수를 가지고 있었다면, this.name을 사용하여 외부 함수의 변수에 접근했을 것

위 예시는 함수가 중첩된 상황에서 파서가 어떻게 변수를 처리하는지 알 수 있으며, 어휘적 범위 지정 Lexical Scoping의 한 예이다

클로저(Closure)

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

 var myFunc = makeFunc();
 //myFunc변수에 displayName을 리턴함
 //유효범위의 어휘적 환경을 유지
 myFunc();
 //리턴된 displayName 함수를 실행(name 변수에 접근)
  • 자바스크립트는 함수를 리턴하고, 함수는 클로저를 형성

  • 클로저는 함수와 함수가 선언된 어휘적 환경의 조합

  • 이 환경은 클로저가 생선된 시점의 유효 범위 내의 모든 지역 변수로 구성된다

      function makeAdder(x) {
         var y = 1;
         return function(z) {
           y = 100;
           return x + y + z;
         };
       }
    
       var add5 = makeAdder(5);
       var add10 = makeAdder(10);
       //클로저에 x와 y의 환경이 저장됨
    
       console.log(add5(2));  // 107 (x:5 + y:100 + z:2)
       console.log(add10(2)); // 112 (x:10 + y:100 + z:2)
       //함수 실행 시 클로저에 저장된 x, y값에 접근하여 값을 계산
  • add5, add10은 둘 다 클로저이다

  • 같은 함수 정의를 공유하지만, 서로 다른 어휘적 환경을 가진다

    프라이빗 메소드(private method)

  • 다른 몇몇 언어들은 private으로 선언할 수 있는 기능을 제공한다. 하지만 JavaScript의 경우 제공되지 않는 기능이기에, 클로저를 통해 흉내낼 수 있다.

     var counter = (function() {
         var privateCounter = 0;
         function changeBy(val) {
           privateCounter += val;
         }
         return {
           increment: function() {
             changeBy(1);
           },
           decrement: function() {
             changeBy(-1);
           },
           value: function() {
             return privateCounter;
           }
         };
       })();
  • 위 예시에서 private은 privateCounter변수와 changeBy함수이다.

  • private 하다 함은.. 외부에서 직접적인 접근이 불가능하여, 반환된 increment,decrement,value를 통해서만 접근이 가능하다.

  • 이러한 방법은 객체지향 프로그래밍의 정보 은닉과 캡슐화 같은 특징, 이점들을 얻을 수 있다.

    var counter1 = counter();
    var counter2 = counter();
    alert(counter1.value()); /* 0 */
    counter1.increment();
    counter1.increment();
    alert(counter1.value()); /* 2 */
    counter1.decrement();
    alert(counter1.value()); /* 1 */
    alert(counter2.value()); /* 0 */

위와 같이 counter1,counter2 서로 다른 두개의 카운터를 만들 수도 있으며, 각 카운터는 독립성을 유지한다.

기타..

  • 위 예시들은 var 키워드로 작성되어, 만약 더 중첩된 클로저를 사용할 경우 문제가 있을 수 있다. let키워드를 사용하여 스코프 범위를 한정하여 사용하자
  • 클로저가 필요하지 않은데, 다른 함수 내에서 불필요하게 작성하는 것은 성능에 부정적인 영향을 미칠 수 있다.
profile
개발자 공부 일기😉

0개의 댓글

Powered by GraphCDN, the GraphQL CDN