클로저

SWP·2022년 5월 12일
0

FE신입면접질문

목록 보기
3/5
post-thumbnail

클로저란 무엇이고, 원리와 왜 사용하나요?

클로저란?

개념적으로 자바스크립트의 모든 함수는 클로저이지만, 실제로 우리는 자바스크립트의 모든 함수를 전부 클로저라고 부르지는 않는다.

클로저란 어떤 함수 A에서 선언한 변수 c를 참조하는 내부함수 B를 외부로 전달할 경우, A의 실행 컨텍스트가 종료된 이후에도 변수 c가 사라지지 않는 현상이 나타나는데, 이 때 B와 같은 함수를 클로저라고 부른다.

해당 함수와 그 함수의 외부를 둘러싸고 있는 렉시컬 환경의 조합이다. 다른 말로하면, 클로저란 내부 함수에서 외부 함수 스코프에 접근할수 있는 권한으로, 함수를 만들고 그 함수 내부의 코드가 탐색하는 스코프를 함수 생성 당시의 렉시컬 스코프로 고정하면 바로 클로저가 되는 것이다

예시)

중요한 부분은 2~4번, 그리고 7번이다. bar는 자신이 생성된 렉시컬 스코프에서 벗어나 global에서 baz라는 이름으로 호출이 되었고, 스코프 탐색은 현재 실행 스택과 관련 없는 foo를 거쳐 갔다. baz를 bar로 초기화할 때는 이미 bar의 outer lexical environment를 foo로 결정한 이후이다. 때문에, bar의 생성과 직접적인 관련이 없는 global에서 아무리 호출하더라도 여전히 foo에서 color를 찾는 것이다. 이런 bar(또는 baz)와 같은 함수를 우리는 클로저라고 부른다.

원리

자바스크립트 엔진의 가비지 컬렉터의 동작 방식 때문이다. 가비지 컬렉터는 쓸모없는 메모리 처리를 끊임없이 수행한다. 어떤 값을 참조하는 변수가 하나라도 있다면 그 값은 수집 대상에 포함하지 않는다.

ex) 즉시호출함수 A에서 선언한 변수 c를 내부함수 B가 참조하고 있으면, c는 A의 실행컨텍스트가 종료되었다 하더라도 가비지 컬렉터의 대상이 되지 않음.

// 이 객체의 프로퍼티가 또 다른 객체를 참조하고 있다면, 프로퍼티가 참조하는 객체는 도달 가능한 값으로 보고, 가비지 컬렉터의 대상에 포함시키지 않는다.

사용하는 이유_ 활용

즉시실행함수의 자유변수(클로저에 의해 참조되는 외부함수의 변수)와 반환을 이용한 클로저를 활용하면
1) 현재 상태를 기억하고 변경된 최신 상태를 유지
2) 전역 변수의 사용 억제
3) 정보의 은닉
등에 활용할 수 있다. 이를 통해 보다 안정적인 프로그래밍이 가능해진다.

1) 현재 상태를 기억하고 변경된 최신 상태를 유지하기 위해

    var toggle = (function () {
      var isShow = false;

      // ① 클로저를 반환
      return function () {
        box.style.display = isShow ? 'block' : 'none';
        // ③ 상태 변경
        isShow = !isShow;
      };
    })();

    // ② 이벤트 프로퍼티에 클로저를 할당
    toggleBtn.onclick = toggle;

2) 전역 변수의 사용 억제

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

    incleaseBtn.onclick = function () {
      count.innerHTML = increase();
    };

3) 정보의 은닉

function Counter() {
  // 카운트를 유지하기 위한 자유 변수
  var counter = 0;

  // 클로저
  this.increase = function () {
    return ++counter;
  };

  // 클로저
  this.decrease = function () {
    return --counter;
  };
}

const counter = new Counter();

console.log(counter.increase()); // 1
console.log(counter.decrease()); // 0


추가_ 렉시컬 환경에 대한 이해 필요.

  • 함수나 블럭 안에는 어떤 변수들이 들어있는지 부모는 누구인지에 대한 정보가 각각의 Object안에 담겨있는데, 이를 렉시컬 환경이라고 한다.
  • 환경 레코드(현재 블럭 내용) + 외부 환경 참조(부모는 누구인지에 대한 정보).
const a = 1; // 전역 스코프
console.log(a); //1
{
  const a = 2; // 블럭 1 스코프
  console.log(a);//2
  {
    const a = 3; // 블럭 2 스코프
    console.log(a);//3
  }
}
  • 코드가 실행이 되면 실행 컨텍스트 스택에 <전역 스코프 렉시컬 환경>이 들어오게 됩니다.
  • <전역스코프 렉시컬환경>이라는 것 안에는 a =1이라는 환경 레코드와
  • null을 가르키고 있는 외부 렉시컬 환경 참조로 이루어져 있습니다.
  • < 블럭 1 스코프 렉시컬 환경>이 스택에 들어오게 되고 a=2라는 환경레코드와
  • 전역을 가르키고 있는 외부 렉시컬 환경 참조로 이루어져 있습니다.
  • 이렇게 스코프가 연결되어 있는것을 스코프 체인이라고 합니다.
  • 동일하게 < 블럭 2 스코프 렉시컬 환경>이 스택에 쌓이게 되고 a = 3 이라는 환경 레코드와
  • 블록 2를 가르키는 외부 렉시컬 환경 참조로 이루어져 있습니다.
  • 따라서 해당 블럭에서 변수에 접근하게 되면 환경 레코드를 확인하여서 그 변수가 있는지 없는지 확인하고 있다면 그 정보를 없다면 외부 렉시컬 환경을 참조해서 값을 가져오게 되는 것입니다.

메모리 절약 뿐만 아니라, 성능을 위해서라도 변수는 최대한 필요한 곳에 정의 하는 것이 필요하다. 왜냐하면 해당 블럭에 없을 경우 스코프 체인을 통해 온 스코프를 다 돌아다녀야 하기 때문입니다.

참고
https://poiemaweb.com/js-closure
https://meetup.toast.com/posts/86
https://hyunseob.github.io/2016/08/30/javascript-closure/
https://velog.io/@graphicnovel/JS-클로저의-의미와-원리-이해-Closure
https://codingsalon.tistory.com/24

profile
잘하고싶다...

0개의 댓글