Javascript - 클로저

13089 단어 jsjs

클로저란?

클로저(closure)는 내부함수가 외부함수의 맥락(context)에 접근할 수 있는 것을 가르키며, 어떤 함수(outer) 내부에 선언된 함수(inner)가 바깥 함수(outer)의 지역변수(outerVariable)를 참조하는 것이 함수(outer)가 종료된 이후에도 계속 유지되는 현상을 말한다.

클로저는 자바스크립트를 이용한 고난이도의 테크닉을 구사하는데 필수적인 개념으로 활용된다.

예제

아래의 코드에 대한 결과는 당연한 결과의 에러코드이다.
name 변수를 outer 이라는 함수의 실행 컨텍스트가 종료 되면서 아무도 접근할 수 없다. name 변수를 함수 밖에서도 사용하기 위해서는 클로져를 사용해서 접근 할수 있다.

function outer(){
  const name = 'test'
  console.log(name)
}
outer() //test
console.log(name) //error

클로져의 특성상 inner함수가 선언될 때 그 주변의 lexical enviroment(여기서는 outer의 Lexical enviroment)와 함께 번들로 묶였기 때문이다.

그렇기 때문에 inner가 실행이 되어서 Lexical environment를 만든 뒤 참조 하지 않아도, 선언할 때 이미 묶여버리게 된다.

function outer(){
  const name = 'test';
  console.log(name)
  return function inner(){
    const greeting = 'hello!'
    console.log(greeting,name)
  }
}
const getTest = outer() //test 
getTest() //hello!test

Lexical Environment

Lexical Environment란 JS Engine이 현재 읽고 있는 코드의 Scope 혹은 Environment를 말한다. 새로운 Lexical Environment는 괄호({})가 있을 때 마다 생성된다. Execution Context가 JS Engine에게 현재의 Lexical Environment를 알려주는 역할을 하고, 이에 맞게 사용할 수 있는 변수를 결정한다. Lexical Environment는 쉽게 생각해서 하나의 새로운 세계라고 생각하면 된다.

const outer = () => {
  const outerVariable = 'outer!'; // 1. 바깥 함수 outer의 스코프에 변수선언

  const inner = () => {
    console.log(outerVariable); // 2. 내부 함수 inner의 스코프에서 스코프체인을 타고 바깥 함수 스코프의 변수 참조
  };

  return inner; // 3. 1급 시민인 함수 inner를 바깥으로 반환
};

const fano = outer(); // 4.  fano에 inner함수의 주소값이 저장됨

fano(); // 5. outer함수 호출은 종료가 되어서 스코프가 사라져야 하지만 outerVariable은 여전히 잘 참조된다.

outer 함수 바깥으로 반환된 inner함수가 outer 함수의 outerVariable 변수를 참조하기에 메모리에 outer의 스코프가 여전히 남아있다..

응용

클로저를 이용하여 프라이빗 메소드를 흉내내는 것이 가능하다. 프라이빗 메소드는 코드에 제한적인 접근만을 허용한다는 점 뿐만 아니라 전역 네임 스페이스를 관리하는 강력한 방법을 제공하여 불필요한 메소드가 공용 인터페이스를 혼란스럽게 만들지 않도록 한다.

아래 코드는 프라이빗 함수와 변수에 접근하는 퍼블릭 함수를 정의하기 위해 클로저를 사용하는 방법을 보여준다. 이렇게 클로저를 사용하는 것을 모듈 패턴이라 한다.

실제 사례로 프론트엔드 프레임워크인 React의 hook API가 클로져를 통해서 구현 되었다. hook은 함수를 여러 번 호출하는 상황에서 데이터를 연속적으로 유지하는 기능이다.

const counterCreator = () => {
  let value = 0;

  return {
    increase() {
      console.log(++value);
    },
    decrease() {
      console.log(--value);
    },
  };
};

const counter = counterCreator();

counter.increase(); // 1
counter.increase(); // 2
counter.decrease(); // 1

아래와 같이 클로저함수 하나로 두 개의 카운터로 생성하여 독립적인 카운터로 만들어 수행 할수 있다.

const counterCreator = () => {
  let value = 0;

  return {
    increase() {
      console.log(++value);
    },
    decrease() {
      console.log(--value);
    },
  };
};

const myCounter = counterCreator();
const yourCounter = counterCreator();

myCounter.increase(); // 1
myCounter.increase(); // 2
yourCounter.increase(); // 1
myCounter.decrease(); // 1

이런 방식으로 클로저를 사용하여 객체지향 프로그래밍의 정보 은닉과 캡슐화 같은 이점들을 얻을 수 있다.

Reference

https://yuddomack.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%81%B4%EB%A1%9C%EC%A0%80Closure?category=754152
https://muscardinus.tistory.com/190
https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures

좋은 웹페이지 즐겨찾기