[JavaScript] 클로저

Wooki·2023년 4월 20일
0

JavaScript

목록 보기
1/7

함수 선언식과 함수 표현식에 대해서 정리하던 중 클로저와 관련된 내용이 나왔고,
클로저와 관련된 내용이 면접 시에도 자주 나온다고 들었던것 같아서 클로저에 대해서 정리해보려 한다.

📕클로저(Closure)

클로저는 자바스크립트에만 있는것이 아닌 자바스크립트처럼 함수를 일급 객체로 취급하는 함수형 프로그래밍 언어등에서 사용되는 중요한 특성이다.

자바스크립트는 객체 지향 프로그래밍 언어이자, 함수형 프로그래밍를 지원한다.

MDN에서 클로저는 "함수와 함수가 선언된 Lexical Scoping의 조합"이라고 말하고 있다.

🔍Lexical scoping?

우선 클로저를 이해하기 위해서 Lexcial scpoing 렉시컬 스코핑에 대해서 이해해야 할것 같다.

렉시컬 스코핑이란, 소스코드 내에서 함수가 선언되는 위치를 기준으로 상위 스코프를 결정하는 것이라고 한다.

 function funcA () {
   var name = "Mozilla";
   function funcB () {
     console.log(name);
   }
   funcB();
 }

함수 B는 함수 A 내부에서 선언된 함수다. 이때 funcB는 funcA의 변수인 name에 대해서 접근할 수 있게된다. 여기서 funcB의 상위 스코프가 funcA이기 때문이다.

funcB함수를 호출하게 되면 funcB의 내부 스코프에서 name 변수를 검색하고, 없다면 스코프 체인을 따라서 상위 스코프인 funcA에서 변수 name을 검색하게 된다.
그래서 funcB함수 내부에 name 변수가 없더라도 정상적으로 실행이 된다.

 function funcA () {
   var name = "Mozilla";
   function funcB () {
     console.log(name);
   }
   return funcB;
 }

var funcLog = funcA(); /// funcLog에 funcB가 할당되며 funcA는 생명주기를 마감
funcLog(); //  console : "Mozilla"

이번경우는 funcA가 funcB를 반환하는 형태의 코드다.
함수의 Life Cycle을 생각해보면 funcA는 변수 funcLog에 함수 funcB를 할당하고 생을 마감했다.

여기서 의문이 생긴다. funcA가 생을 마감할때 funcA의 지역변수인 name도 생을 마감하여 더이상 유효한 변수가 아닐텐데 funcLog에 할당된 funcB를 실행시키면 funcA 의 지역변수인 name에 대해 정상적으로 참조하고 있다.

✔️클로저

이런 경우에서 사용되는 개념이 클로저 인것 같다.

외부함수보다 내부함수가 더 오래 유지되는 경우, 외부 함수 밖에서 내부 함수가 호출되더라도 외부함수의 지역 변수에 접근할수 있는 함수 => 클로저(Closure)

클로저는 자신이 선언되었을 때의 환경 (렉시컬 환경)을 기억하고 있는 함수 정도로 정리하면 될 듯 하다.


💡클로저가 사용될 때

클로저는 렉시컬 환경 속의 어떤 데이터와 그 데이터를 조작하는 함수를 연관시켜 줄 때 유용하게 사용할 수 있다.

상태 유지와 불필요한 전역 변수의 사용을 막아줄 수 있다.

function makeAlert(hour) {
  var time = hour;
  var count = 1;
  return function () {
    console.log(time + "시 " + count++ + "번째 알람");
  };
}

var alert9 = makeAlert(9);
var alert13 = makeAlert(13);

alert9(); // 9시 1번째 알람
alert9(); // 9시 2번째 알람
alert9(); // 9시 3번째 알람

alert13(); // 13시 1번째 알람
alert13(); // 13시 2번째 알람
alert13(); // 13시 3번째 알람

console.log(time); // time is not defined
console.log(count); // count is not defined

makeAlert는 timecount를 변수로 가지고 클로저를 반환하는 함수다.
alert9alert13은 각각 makeAlert 함수가 호출되어 선언되는 시점의 환경을 기억하고 있기 때문에

alert9, alert13 각각 count와, time이라는 상태를 유지할수 있다.

클로저가 없었다면 이 값들을 전역 변수를 사용해서 구현해야 할 것이고

전역변수는 어디서든 접근하여 부작용을 일으키거나 의도하지 않은 부수 효과(Side Effect)를 발생시킬 수 있기 때문에 사용을 억제하는 것이 좋다.

private 메소드 흉내내기

자바와 같은 몇몇 언어들은 메소드를 private로 선언할 수 있는 기능을 제공하는데,

private 메소드
같은 클래스 내부에서만 그 메소드를 호출할 수 있게 하는 것

자바스크립트는 위와 같은 기능을 내부적으로 제공하지 않는다.
그렇지만 클로저를 이용하면 비슷한 기능을 하도록 구현할 수 있다고 한다.

   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는 privateCounter 변수와 changBy 함수, 두개의 값을 같은 환경으로 가지는 클로저 3개를 객체에 담아서 반환한다.

객체를 반환하더라도 클로저들의 렉시컬 환경인 privateCounter changeBy는 메모리에 남게 되고,

return된 객체의 [increment,decrement,value] 세 함수를 통해서만
privateCounter변수와 changBy함수에 접근할 수 있게 된다.
이로서 privateCounter와 그를 조작하는 함수 changBy를 은닉시킬 수 있다.

이런 방식으로 클로저를 이용하면 객체지향 프로그래밍의 정보 은닉캡슐화를 얻을 수 있다고 한다.


👋마무리

이 글을 작성하면서 MDN Web docsPoiemaWeb을 주로 참고했는데 아직 완벽히 이해하진 못한것 같다. 실행 컨텍스트에 대한 내용을 좀 더 공부하면 클로저를 더 깊게 이해할 수있을것 같아서 근 시일 내로 실행 컨텍스트에 대해서도 정리해볼까 함.

아직 클로저를 적절한 상황에서 잘 쓸수 있을것 같진 않지만 대략적인 쓰임새는 파악한 것 같은 느낌..?

profile
웹 개발자

0개의 댓글