JS_클로저

박채연·2024년 5월 4일

프론트엔드 기초

목록 보기
5/10

클로저의 정의

“A closure is the combination of a function and the lexical environment within which that function was declared.”
클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.

MDN의 정의입니다.

여기서 렉시컬 환경이 무엇인지 생소합니다.

렉시컬 스코프

렉시컬 스코프란 함수가 선언이 되는 위치에 따라서 상위 스코프가 결정되는 스코프입니다.

또 다른 정의

클로저의 상황 자체를 서술하는 정의입니다.

내부함수는 외부함수의 변수를 참조하는데 외부함수의 수명에 다했음에도 내부함수는 외부함수의 변수에 참조 가능한 상태입니다.

예시

const outer = function(){
	const a = "내부변수";
  	const inner = function(){
    	console.log(a);
    }
    
    return inner;
}

const myFunc = outer();
myFunc();
  1. 전역컨텍스트가 만들어지고 컨텍스트 생성 시 컨텍스트 안에 변수객체(arguments, variable), scope chain, this가 생기게 됩니다.
// 전역컨텍스트를 객체화
'전역 컨텍스트': {
  변수객체: {
    arguments: null,
    variable: ['outer','myFunc'],
  },
  scopeChain: ['전역 변수객체'],
  this: window,
}
  1. outer()를 실행하였고 outer 함수 컨텍스트가 생성됩니다.
// outer 컨텍스트를 객체화
'outer 컨텍스트': {
  변수객체: {
    arguments: null,
    variable: [{ a : '내부변수' },'inner'],
  },
  scopeChain: ['outer 변수객체','전역 변수객체'],
  this: window,
}
  1. myFunc()가 실행되고 myFunc 함수 컨텍스트가 만들어집니다.
// outer 컨텍스트를 객체화
"closure 컨텍스트":  {
  변수객체: {
    arguments: null,
    variable: null,
  scopeChain: ['myFunc 변수객체', 'outer 변수객체', '전역 변수객체'],
  this: window,
}

이 때 스코프 체인을 통해 outer 변수 객체에 접근할 수 있고 이를 통해 a라는 변수가 접근이 가능한 것입니다.

MDN의 정의 이해해보자.

함수와 함수가 선언이 되었을 때 렉시컬 환경과의 조합입니다.

여기서 함수란 반환되는 내부 함수를 의미합니다.
그리고 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때 환경 밖에서도 접근할 수 있는 함수를 뜻합니다.

클로저는 자신이 생성될 때의 환경(Lexical environment)을 기억하는 함수다라고 말할 수 있습니다.

클로저의 예시

1. 전역변수의 사용 억제

<!DOCTYPE html>
<html>
  <body>
  <p>클로저를 사용한 Counting</p>
  <button id="inclease">+</button>
  <p id="count">0</p>
  <script>
    var incleaseBtn = document.getElementById('inclease');
    var count = document.getElementById('count');

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

    incleaseBtn.onclick = function () {
      count.innerHTML = increase();
    };
  </script>
</body>
</html>

counter라는 값을 전역상태로 놓을 경우 수정이 가능하게 됩니다.
이 값을 은닉화하지만 상태 유지가 가능하게 (이전 값을 기억하게) 하고 싶다면, 클로저를 활용할 수 있습니다.

2. React의 useState

function useState(initialValue) {
  let _value = initialValue;

  const state = () => _value;
  const setState = (newValue) => {
    _value = newValue;
  };

  return [state, setState];
}

const [count, setCount] = React.useState(1);
console.log(count()); // 1
setCount(2);
console.log(count()); // 2

useState의 원리가 되는 코드입니다.

클로저를 통해 간단하게 구현한 useState 함수입니다.
[state, setState]가 선언되는 시점에서 useState의 호출은 끝나게 되지만, 클로저가 내부의 _value 값을 기억하고 있기 때문에 이후에도 접근이 가능합니다.

클로저의 단점

가비지콜렉터에서 대상이 제외되기 때문에 클로저를 남용하는 것은 좋지 않습니다.
메모리 누수의 위험이 있을 수 있습니다.

참고

https://blacklobster.tistory.com/7
https://www.zerocho.com/category/JavaScript/post/5741d96d094da4986bc950a0
https://poiemaweb.com/js-closure
https://enjoydev.life/blog/javascript/6-closure

profile
기록장입니다.

0개의 댓글