[TIL] 자바스크립트의 클로저란?

Leesu·2023년 2월 26일
0

[TIL] : Today I Learned

목록 보기
9/21

매번 매번 매번 봐도 남에게 설명해주기 어려운 그것 .. 클로저

클로저란?

클로저는 외부 변수를 기억하고 이 외부 변수에 접근할 수 있는 함수를 의미한다.

클로저는 내부함수가 외부함수의 맥락(context)에 접근할 수 있는 것을 가리키며 캡슐화나 은닉화가 핵심이다.

  • 스코프 체인으로 표현되기도 한다.

  • 들어가기 앞서, 스코프에 대해 간단히 설명.

    • 스코프란, 변수와 함수의 유효범위를 말한다.
    • 스코프는 변수나 함수가 선언된 위치를 말하며, 선언된 위치에 따라 전역스코프, 함수 스코프, 블록 스코프 등으로 다양하다.

- 스코프 접근 범위 3단계

  1. 자신에 대한 접근(자신의 블럭 내에 정의된 변수) = 지역 범위
  2. 외부 함수의 변수에 대한 접근 = 외부 함수 범위
  3. 전역 변수에 대한 접근 = 전역 범위

- 클로저 특징

  1. 함수 내부에서 선언된 변수들은 해당 함수의 실행이 끝나도 유지된다.
  2. 함수를 반환하는 함수를 작성하여 클로저를 생성할 수 있다.
  3. 클로저를 사용하면, 함수 외부에서 접근할 수 없는 비공개 변수를 만들어 사용할 수 있다.
  4. 클로저는 함수를 호출할 때마다 매번 새로운 스코프를 생성한다.

- 예제(1)

아래는 내부 함수에서 외부 함수에 직접 접근할 수 있는 예시이다.

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

설명

  • 위의 코드에서 볼 수 있다시피, init() 은 지역 변수 name 과 함수 displayName() 을 생성한다.
  • displayname()init 안에 정의된 내부 함수이며 init() 함수 본문에서만 사용할 수 있다.
  • 여기서 주의할 점은 displayName() 내부엔 자신만의 지역 변수가 없다는 점인데,
    그런데 함수 내부에서는 외부함수에 접근할 수 있기 때문에 displayName() 역시 부모함수 init()에서 선언된 변수 name에 접근할 수 있다!

따라서,

위 코드를 실행하면 displayName() 함수 내의 alert() 문이 부모 함수에서 정의한 변수 name의 값을 성공적으로 출력한다.

  • 이는 어휘적 범위지정(lexical scoping)의 한 예시이다.
    • "lexical" 이런 어휘적 범위지정 과정에서 변수가 어디에서 사용가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지 고려한다는 것을 의미한다.

- 예제(2)

아래는 함수 내부에서 선언된 변수를 외부에서 직접 접근할 수 없도록 하고자 할 때 클로저를 활용한 예시이다.

function counter() {
  let count = 0;
  
  function increaseCount() {
    count++;
    console.log(count);
  }
  
  return increaseCount;
}

const myCounter = counter();
myCounter(); // 1
myCounter(); // 2

설명

  • 위의 코드에서, counter() 함수는 increaseCount() 함수를 반환한다. 이때 increaseCount() 함수는 외부에서 선언된 변수 count에 접근할 수 있도록 클로저를 형성한다.
  • 이후 myCounter 변수에 counter() 함수를 실행한 결과를 할당하고, myCounter 함수를 호출할 때마다 counter 변수가 증가하도록 구현이 되어있는 것이다.

따라서,

  • 클로저를 이용하여 함수 내부에서 선언된 count 변수를 외부에서 직접 접근할 수 없게 만들고, 함수를 통해 안전하게 증가하는 변수를 만들 수 있다.

- 예제(3)

다음은 데이터 은닉화를 위해 클로저를 활용한 예시이다.

<!DOCTYPE html>
<html>
  <body>
  <button id="plus">+</button>
  <p id="count">0</p>
  <script>
    let plusBtn = document.getElementById('plus');
    let countTxt = document.getElementById('count');

    let plus= (function () {
      // 카운트 상태를 유지하기 위한 자유 변수
      let count = 0;
      // 클로저를 반환
      return function () {
        return count++;
      };
    }());

    plusBtn.onclick = function () {
      countTxt .innerHTML = plus();
    };
  </script>
</body>
</html>
  • 위의 코드는 숫자를 하나씩 카운트하는 코드인데, 여기서 count 라는 변수가 만약 전역변수로 선언되어 카운팅되게 만들어졌다면 이는 위험한 프로그램이 될 것이다.

  • 그런데 위처럼 클로저를 이용하면 내부로 철저히 숨길 수 있어 은닉화가 가능하다.


참고자료_1
참고자료_2
참고자료_3

profile
기억력 안 좋은 FE 개발자의 메모장

0개의 댓글