[Javascript ]Closure

도비굴·2023년 2월 20일
0
post-thumbnail

클로저(Closure)를 알아보자

클로저(Closure)

자바스크립트 클로저(closure)는 함수가 실행될 때 함수 내부에서 생성한 변수와 함수를 외부에서 접근할 수 없도록 보호하면서도, 이러한 변수와 함수를 참조할 수 있는 메커니즘을 말한다.
클로저를 이해하기 위해선 스코프의 개념을 먼저 알아야한다.

스코프란 뭘까

스코프는 참조 대상 식별자(identifier, 변수, 함수의 이름과 같이 어떤 대상을 다른 대상과 구분하여 식별할 수 있는 유일한 이름)를 찾아내기 위한 규칙이다. 자바스크립트는 이 규칙대로 식별자를 찾는다.
클로저를 알기 위해선 아래의 내용을 알아야 한다.

함수 스코프 function-scoped (var)
함수 내에서 선언된 변수만 그 지역변수가 된다.(var는 유일하게 함수 스코프내에서 지역변수가 된다. 이건 걍 신기해서 넣어놓음)

var a = 10;     // 전역변수

(function () {
  var b = 20;   // 지역변수
})();

console.log(a); // 10
console.log(b); // "b" is not defined

블록 스코프 block-scoped (let,const, 함수, if, for, while, try/catch등)
모든 코드블록에서 선언된 변수는 해당 코드블록 내에서만 유효함

스코프를 더 자세히 보자면 너무 길어지니 여기까지 하겠다.

이제 ㄹㅇ 클로저를 알아보자

함수가 실행될 때 렉시컬환경이 구성되는데 이를 활용해서 내부함수에서 생성한 변수와 외부함수에서 생성한 변수를 참조할 수 있는 매커니즘이다.

아래의 예를 보자

 function makeAdder(x) {
      return function (y) {
        return x + y;
      };
    }
    const add3 = makeAdder(3);
    console.log(add3(2)); //5
    // -> add3 함수가 생성된 이후에도
    // 상위함수인 makeAdder의 x에 접근 가능

    const add10 = makeAdder(10);
    console.log(add10(5)); //15
    console.log(add3(1)); //4

위의 코드에서 add3이라는 함수를 makeAdder라는 함수를 참조해 생성했을 때 클로저가 형성되는 것이다.

함수가 생성될 당시의 외부 변수(매개변수)를 기억하고 이 변수를 생성이후에도 계속 접근이 가능하게 하는 것

클로저는 어떻게 활용할 수 있을까

이러한 클로저는 주로 콜백(callback) 함수, 비동기 처리(async) 및 모듈 패턴(module pattern)에서 많이 사용된다.

1. 비동기 처리 (Asynchronous operations)


function delay(message, duration) {
  setTimeout(function() {
    console.log(message);
  }, duration);
}

let message = "Hello, world!";
delay(message, 1000);

위 코드에서 setTimeout 함수의 콜백 함수는 클로저를 사용하여 message 변수를 참조한다. 이 때, 클로저는 함수 외부의 변수를 함수 내부에서 계속해서 참조할 수 있도록 한다. 따라서 delay 함수가 호출될 때 message 변수를 설정하고, setTimeout 함수가 호출될 때까지 변수를 보존할 수 있다.

2. 프라이빗 변수 (Private variables)
클로저를 사용하여 객체의 프라이빗 변수를 구현할 수 있다. 예를 들어, 다음과 같은 코드에서는 counter 객체를 정의하고 있다. 이 객체는 count 변수와 increment 메소드를 가지고 있다. count 변수는 클로저를 사용하여 객체 외부에서는 접근할 수 없는 프라이빗 변수로 설정되어 있다. increment 메소드는 count 변수를 증가시키는 역할을 한다.

function createCounter() {
  let count = 0;

  return {
    increment: function() {
      count++;
    },

    getCount: function() {
      return count;
    }
  };
}

const counter = createCounter();
counter.increment();
console.log(counter.getCount()); // 1

위 코드에서 createCounter 함수는 count 변수를 클로저로 참조하는 객체를 반환합니다. 이 때, 클로저는 count 변수를 보존하여 increment 메소드가 호출될 때마다 count 변수를 증가시킬 수 있습니다. getCount 메소드는 count 변수의 값을 반환하는 역할을 합니다. 이 때, 클로저는 count 변수를 외부에서 접근할 수 없도록 보호합니다.


let message = 'Hello, world!'

function delay(message, duration) {
  setTimeout(function() {
    console.log(message);
  }, duration);
}

delay("Hello, world!", 1000);

내가 느끼기에 클로저에서 중요한 것은 특정 변수를 외부로부터 보호할 수 있다는 것이다.
위에서 봤던 예시에서 message라는 변수가 어떤시점에 어떤값으로 변경될지 모를 수도 있는 상황에 delay호출한 시점의 message는 delay함수 블럭안에선 불변할 수 있다.


React에서의 Closure

React의 useState, useRef 훅 등은 클로저를 사용하여 상태(state)를 관리하거나 DOM 요소에 접근하는 등의 작업을 수행한다.

useState의 내부코드를 살펴보자

let state; // 현재 상태값을 저장할 변수
function useState(initialValue) {
  state = state || initialValue; // 초기값 설정

  function setState(newState) {
    state = newState; // 새로운 상태값 저장
    render(); // 상태가 변경되었으므로 컴포넌트를 다시 렌더링
  }

  return [state, setState]; // 상태값과 setState 함수를 배열로 반환
}

클로저 함수를 통해 setState 에서 변경한 state의 값을 저장한다.

위와 같이 클로저를 이용한 함수를 만들 수 있다.

profile
개발굴

0개의 댓글