[JS] 클로저

Urther·2021년 12월 11일
0

JavaScript

목록 보기
2/8

클로저

함수와 함수가 선언되는 렉시컬 환경의 조합

함수가 생성되면 스코프가 존재하는데, 함수는 호출되기 전부터 자신의 [[environment]] 라는 내부 슬롯을 안다. ( 렉시컬 환경 ― 자신이 호출되는 곳(정적 스코프)에서 상위 스코프가 결정되는 것이 아니라, 자신이 선언되는 곳에서 상위스코프가 결정된다. )

즉, 자신의 상위 스코프를 기억하고 있기 때문에 외부에서 참조를 해도 [[environment]]를 통해 접근이 가능하다는 것이다.

실행 컨텍스트 스택에서 Outer 함수가 pop되면 가비지 컬렉터가 발생하여 메모리에서 해제가 된다. 하지만 전역 변수에 있는 const innerFunc가 inner 함수를 참조하기 때문에 inner의 [[environment]] 로 가르키는 outer 역시 가비지 컬렉터의 대상에서 벗어나는 것이다.

위와 같은 중첩함수가 클로저라고 한다.

클로저가 될 수 있는 조건

위에서도 언급했지만 MDN에서 클로저는 함수와 함수가 선언되는 렉시컬 환경이라고 정의했다. JS에서 함수는 모두, 자신의 상위스코프를 알고 태어나기 때문에 JS는 모두 클로저라고 할 수 있을까?
이론적으로 맞지만 통상적으로 그것을 클로저라고 부르진 않는다.

  1. 중첩 함수가 외부 함수보다 오래 생존
  2. 상위 스코프의 변수를 하나 이상 참조해야 한다.
function foo() {
  const x = 1;
  function bar() {
    console.log(x);
  }
  bar();
}

foo();

위와 같은 예제에서 bar는 자신의 상위 스코프에 있는 x ― foo 에 존재하는 ― 를 참조한다. 그러면 이것을 클로저라고 부를 수 있을까?
변수의 생명주기를 고려하면 foo를 호출하고 bar로 반환되지 않기 때문에 bar의 생명주기는 foo 보다 짧다. 그렇기 때문에 클로저라고 부를 수 없다.

클로저가 유용할 때 !

상태를 안전하게 변경하고 유지

순수 함수(외부의 상태를 변경하지 않는 함수)를 지향해야 하는데 클로저를 통해 의도치 않게 상태를 변경시키지 않을 수 있다. ― 특정 함수만 상태 변경 허용


전역에 선언된 num 값이 upNum이라는 함수를 통해 변경된다면 이것은 비순수함수이고, 지양해야한다.

이렇게 즉시 실행 함수로 변경해주게 되면, upNum 의 return 안에 있는 function의 [[environment]] 가 생존해있을 수 있기 때문에 num 값을 계속 교체해줄 수 있다.

클로저 = 즉시 실행함수?

// 함수를 인자로 전달받고 함수를 반환하는 고차 함수
// 이 함수가 반환하는 함수는 클로저로서 카운트 상태를 유지하기 위한 자유 변수 counter을 기억한다.
function makeCounter(predicate) {
  // 카운트 상태를 유지하기 위한 자유 변수
  var counter = 0;
  // 클로저를 반환
  return function () {
    counter = predicate(counter);
    return counter;
  };
}

// 보조 함수
function increase(n) {
  return ++n;
}

// 보조 함수
function decrease(n) {
  return --n;
}


const increaser = makeCounter(increase);
console.log(increaser()); // 1
console.log(increaser()); // 2


const decreaser = makeCounter(decrease);
console.log(decreaser()); // -1
console.log(decreaser()); // -2

즉시 함수를 사용하는 예와 다르게 increase , decrease (함수)를 매개변수로 받는 경우 new 를 2번 호출했기 때문에 실행컨텍스트에는 makeCounter를 두번 호출한다. 이렇게 되면 increser의 counter와 decrease counter의 스코프가 달라지기 때문에 변경 사항이 서로 연동이 되지 않는다는 문제점이 있다.

반면, 즉시 실행 함수는 call stack(실행컨텍스트 스택)에 들어오는 것은 같지만 new 를 통해 호출해줄 필요가 없기 때문에 렉시컬 환경이 동일해질 수 있다.

const counter = (function(){
  let num = 0 ;
  return{
    increase(){
      return ++num;
    }
    decrease(){
      return --num;
    }
};
} ());

접근을 new counter() 대신 console.log(counter.increase()) , console.log(counter.decrease()) 로 해주면 된다.

결론

결론적으로 나의 생각은 클로저를 이용 장려해야 한다 !!

완전히 다르지만 JAVA라는 단어가 앞에 붙어서 비슷할 것이라고 생각하는 java는 private를 통해 정보은닉이 가능하지만, 자바스크립트에서는 private 대신 모두 public이기 때문에 정보은닉이 가능하지 않았다. 하지만, 클로저를 통해 얼추 private를 흉내낼 수 있게끔 되었다.

하지만 완전한 100%의 정보 은닉이 가능 한 것은 아니다.


poiemaweb - 모던 자바스크립트, 캡슐화

profile
이전해요 ☘️ https://mei-zy.tistory.com

0개의 댓글