[Javascript] 클로저(Closure)

채린·2023년 4월 11일
1

Javascript

목록 보기
2/4
post-thumbnail

❓ 클로저(Closure) 란?

클로저(Closure)는 함수와 함수가 선언된 어휘적(lexical) 환경의 조합이다. 즉, 함수가 생성될 당시의 외부 변수를 기억하여 생성 이후에도 계속 접근 가능한 것이다.

  • Lexical Environment 란? - 코드 block, function, script 를 실행하기 앞서 생성되는 특별한 객체로, 실행할 스코프 범위 안에 있는 변수와 함수를 프로퍼티로 저장하는 객체
 <script>
  let one = 'one'; 
  function fn1() {
    let two = 'two';
    console.log(one, two);  // one two 
  }
  fn1(); 
</script> 


  • 내부 lexical 환경에서 찾은 다음 없으면 전역 lexical 환경을 참조하는 것을 알 수 있음
<script> 
  let one = 'one';
  function fn2() {
    let three = 'three'; 
    console.log(one, two, three); // two is not defined   
  }
  function fn1() {
    let two = 'two';
    console.log(one, two); // one two 
    fn2();   
  }
  fn1();
</script> 

  • fn2()가 fn1() 안에서 실행됐다고 해서 fn1() 함수에 있는 변수를 참조할 수 있는 것이 아님

    즉, 함수는 어디서 실행됐는지가 중요하지 않음

  • 그렇다면 함수안에 함수를 선언하면 어떨까?
<script>
  let one = 'one';

  function fn1() {
    function fn2() {
      let three = 'three';
      console.log(one, two, three); // one two three  
    }
	let two = 'two'
	console.log(one, two)  // one two  
    fn2(); 
  }
  fn1();
</script> 

  • 이렇게 하게 되면 아까는 없던 Closure가 Local과 Global 사이에 생겨서 fn1() 의 변수를 참조할 수 있게 됨 (Closure 안에 함수의 local 이 저장되어 있음)
  • 클로저의 특성상 그 주변의 lexical enviroment 와 함께 번들로 묶였기 때문

즉, 함수의 유효범위는 함수가 어디서 실행됐는지가 아니라 어디서 정의됐는지에 따라 달라짐

👀 클로저(Closure)가 필요한 이유

1) 전역 변수의 사용 억제

  • 전역 변수로 공유될 변수를 작성하다 보면 누구나 이 전역변수에 접근할 수 있기 때문에 의도치 않게 값이 변경되어 오류가 발생할 확률이 높아짐
  • 클로저를 이용하면 이러한 문제를 해결할 수 있음

    즉, 전역 변수의 사용을 억제해 상태 변경이나 가변 데이터를 피하고 안정성을 증가 시킬 수 있음

const btn = document.querySelector('button')

btn.addEventListener('click',handleClick())

function handleCilck(){
  let count = 0
  return function(){
    count++
    return count
  }
} 

2) 데이터의 은닉화

  • 클로저를 사용하지 않았을 때
function Hello(name) {
  this._name = name;
}

Hello.prototype.say = function() {
  console.log('Hello, ' + this._name);
}

let hello1 = new Hello('채린');
let hello2 = new Hello('채채');
let hello3 = new Hello('째째');

hello1.say(); // 'Hello, 채린'
hello2.say(); // 'Hello, 채채'
hello3.say(); // 'Hello, 째째'
hello1._name = 'anonymous';
hello1.say(); // 'Hello, anonymous'

이렇게 하게되면 외부에서 쉽게 접근 가능하게 됨

  • 클로저를 사용했을 때
function hello(name) {
  var _name = name;
  return function() {
    console.log('Hello, ' + _name);
  };
}

let hello1 = hello('채린');
let hello2 = hello('채채');
let hello3 = hello('째째');

hello1(); // 'Hello, 채린'
hello2(); // 'Hello, 채채'
hello3(); // 'Hello, 째째'
  • 클로저를 사용하여 외부에서 변수에 직접 접근하는 것을 제한할 수 있음

3) 상태유지

  • 현재 상태를 기억하고 있다가 상태가 변경되면 그것을 최신상태로 유지함
  • React 에서 상태관리로 사용되는 useState가 이를 의미함
<!DOCTYPE html>
<html>
<body>
  <button class="toggle">toggle</button>
  <div class="box" style="width: 100px; height: 100px; background: red;"></div>

  <script>
    var box = document.querySelector('.box');
    var toggleBtn = document.querySelector('.toggle');

    var toggle = (function () {
      var isShow = false;

      // ① 클로저를 반환
      return function () {
        box.style.display = isShow ? 'block' : 'none';
        // ③ 상태 변경
        isShow = !isShow;
      };
    })();

    // ② 이벤트 프로퍼티에 클로저를 할당
    toggleBtn.onclick = toggle;
  </script>
</body>
</html>

❔ 클로저의 단점

클로저를 사용하면 매번 새로운 함수가 리턴되어 같은 기능을 함수가 다른 메모리 공간을 차지한다고 보면 된다. 그렇기 때문에 성능 측면을 고려했을 때 꼭 필요한 경우가 아니라면 클로저를 사용하지 않는 것이 좋다.

📚 참고

https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures
https://www.youtube.com/watch?v=bwwaSwf7vkE&t=133s
https://devkingdom.tistory.com/331
https://hyunseob.github.io/2016/08/30/javascript-closure/

Image by Mohammad Sheyriyar Shah from Pixabay

0개의 댓글