본문 바로가기
개발/Javascript

[JavaScript] 클로저(Closure)

by 그레이웅 2022. 10. 6. 00:20
반응형

클로저(Closure)

클로저는 함수내에서 함수가 선언된 어휘적 환경의 조합이다. 

클로저를 이해하기 위해선 우선 자바스크립트의 변수의 유효 범위의 지정을 먼저 이해해야 한다.

 

어휘적 범위 지정(Lexical scoping)

어휘적 범위 지정은 변수가 어디에서 사용 가능한지 알기 위해서 그 변수가 소스 코드 내에 어디에 선언되었는지 고려하는 것을 의미한다.

 

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

init 함수에는 지역변수 name , displayName이 있다.

내부 함수로 정의된 displayNameinit 함수 안에 정의된 내부함수이다. 

위의 예제를 실행하면 내부 함수의 alert()문이 부모 함수 init에 정의한 변수 name의 값을 출력한다.

 

위의 예제를 보면 중첩된 함수는 외부 범위(scope)에서 선언한 변수에 접근할 수 있다는 것을 알 수 있다.

 

let과 const를 사용한 범위지정

 

ES6이전의 Javascript에서는 전역 스코프와 함수 스코프 두가지만 존재하였다.

var는 함수 블록 안에 선언되었는지 밖에 선언되었는지에 따라 전역과 함수 스코프로 나누어졌다.

 

    if (Math.random() > 0.5) {
      var x = 1;
    } else {
      var x = 2;
    }
    console.log(x);

위의 예제에서의 x는 C나 Java에서와 같이 블록안에서만 선언된 변수는 블록안에서만 사용가능해야하지만 var로 선언한 변수에대해서는 블록 스코프를 생성하지 않기때문에 여기서의 var명령문은 전역변수를 생성한다. 그래서 에러같은 내용이지만 에러를 내뱉지는 않는다.

 

반면 let과 const는 ES6문법에서 블록 스코프를 생성할 수 있도록 도입되었다.

 

  if (Math.random() > 0.5) {
      const x = 1;
    } else {
      const x = 2;
    }
    console.log(x); // ReferenceError: x is not defined

ES6문법에서는 블록단위의 스코프가 let과 const 변수에서만 유효하다. 클로저는 이 모든 변수의 스코프를 캡쳐할 수 있다.

 

클로저(closure)

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

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

 

이 코드는 이전 예제와 동일한 결과를 실행하지만, makeFunc함수가 리턴되어 myFunc 변수에 저장되는 점이다.

 

위의 예제는 다른점이 자바스크립트 함수를 리턴하여 리턴하는 함수가 클로저를 형성하기 때문에 위의 예제와는 다르다.

 

일반적으로는 함수의 실행이 끝나면 변수가 사라진다.

하지만 위의 예시는 자바스크립트의 함수를 리턴하여, 리턴하는 함수가 클로저를 형성하기 때문에 변수 name에 있는 "mozilla"라는 값이 남아있게 된다.

 

클로저의 활용

클로저를 활용하면 프라이빗 메소드를 구현할 수 있다.

자바스크립트에서는 프리이빗 메소드를 지원할 수 없지만, 클로저를 활용하면 이를 구현할 수 있다.

 

 var counter = (function() {
      var privateCounter = 0;
      function changeBy(val) {
        privateCounter += val;
      }
      return {
        increment: function() {
          changeBy(1);
        },
        decrement: function() {
          changeBy(-1);
        },
        value: function() {
          return privateCounter;
        }
      };
    })();

    console.log(counter.value()); // logs 0
    counter.increment();
    counter.increment();
    console.log(counter.value()); // logs 2
    counter.decrement();
    console.log(counter.value()); // logs 1

이 예제는 counter.value가 공유되는 어휘적 환경을 만들어준다.

 

공유되는 어휘적 환경은 실행되는 익명 함수 안에서 만들어지며, 익명함수는 정의되는 즉시 실행된다. 

changeBy라는 함수와  privateCounter 라는 변수가 프라이빗 아이템이다.

이 둘다 외부에서 공유 될 수 없으며, 익명 래퍼에서 반환된 퍼블릭 함수에서만 접근되어야 한다.

 

 

이 예제를 이용하여 여러개의 카운터를 만들 수 있다.

   var makeCounter = function() {
      var privateCounter = 0;
      function changeBy(val) {
        privateCounter += val;
      }
      return {
        increment: function() {
          changeBy(1);
        },
        decrement: function() {
          changeBy(-1);
        },
        value: function() {
          return privateCounter;
        }
      }
    };

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

두 가지의 counter 는 개별로 독립성을 가진다.

하나의 클로저의 값을 변경해도 다른 클로저에는 영향을 끼치지 않는다.

 

 

참고한 글

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

반응형

'개발 > Javascript' 카테고리의 다른 글

[Javascript] 자바스크립트의 메모리관리  (0) 2022.10.17
[JavaScript] Event Loop(이벤트 루프)  (0) 2022.10.07
[JavaScript] 호이스팅(Hoisting)  (0) 2022.10.06
WebStorage API  (0) 2022.09.30
Babel과 polyfill  (1) 2022.09.29

댓글