본문 바로가기

개인공부/프론트엔드

클로저

반응형

클로저

- 내부함수가 외부함수의 맥락에 접근할 수 있는 것을 가르킨다. 클로저는 자바스크립트를 이용한 고난이도의 테크닉을 구사하는데 필수적인 개념으로 활용된다.

- 프라이빗한 변수를 만들때 사용한다 getter, setter같은것

 

부연설명을 좀더하자면 클로저는 JS만의 고유한 개념이 아니라 함수를 일급 객체로 취급하는 함수형 프로그래밍 언어 : 얼랭, 스칼라, 하스켈에서 사용되는 중요한 특성중의 하나이다. 함수형 프로그래밍이란 프로그래밍 방법론중 하나이며 일정한 변수에 대해서 항상같은 결과가 나온다는 개념이다. ( 일급 객체란 함수자체를 인자로 받거나, 반환할 수있고 변수에 할당할 수 있는 객체를 의미한다. 이것이 가능하기 때문에 고차함수, 콜백함수도 가능하다.)

 

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

 

즉 외부함수가 이미 반환되었어도 외부함수 내의 변수는 이를 필요로 하는 내부함수가 하나 이상 존재하는 경우 계속 유지된다. 이때 내부함수가 외부함수에 있는 변수의 복사본이 아니라 실제 변수에 접근한다는 것에 주의하여야 한다.

 

내부함수

function outter(){
    function inner(){
        var title = 'coding everybody'; 
        alert(title);
    }
    inner();
}
outter();

 

이러한 테크닉은 크롬에서 사용자는 호출못하고 초반의 한번 실행되는 함수에 유용하다.

 

function outter(){
    var title = 'coding everybody';  
    return function(){        
        alert(title);
    }
}
inner = outter();
inner();

return은 함수를 return하고 있다. 일반적으로는 return을 한 함수는 종료가 되지만 inner라는  녀석은 outer()라는 녀석은 사라지지만 inner 내부 함수를 통해서 접근 할 수 있다.

 

그래서 이거를 어디에 쓸까??

 

function factory_movie(title){
    return {
        get_title : function (){
            return title;
        },
        set_title : function(_title){
            title = _title
        }
    }
}
ghost = factory_movie('Ghost in the shell');
matrix = factory_movie('Matrix');
 
alert(ghost.get_title());
alert(matrix.get_title());
 
ghost.set_title('공각기동대');
 
alert(ghost.get_title());
alert(matrix.get_title());

factory_movie는 객체를 return하는 타입이다. 각각의 객체는 함수를 가지고 있다. 따라서 title의 값은 함수 외부에서 어떻게 쓰든 title의 값을 바꿔도 그 함수를 받은 변수는 유일하게 사용하고 변수 변경도 가능 하다.

 

var arr = []
for(var i = 0; i < 5; i++){
    arr[i] = function(){
        return i;
    }
}
for(var index in arr) {
    console.log(arr[index]());
}
var arr = []
for(var i = 0; i < 5; i++){
    arr[i] = function(id) {
        return function(){
            return id;
        }
    }(i);
}
for(var index in arr) {
    console.log(arr[index]());
}

  최외각 함수에서 i를 인자로 넘겨주고 그값을 id로 받고 그 id 값을 return하는 함수를 리턴한다. 이때 id값은 외부와 연결된 (C언어에서 point 가 아니라 내부에서 새로 생성된 변수) 이다.

 

언제쓰나?

 

상태유지

<!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>

전역 변수의 사용 억제

<!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');

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

    incleaseBtn.onclick = function () {
      count.innerHTML = increase();
    };
  </script>
</body>
// 함수를 인자로 전달받고 함수를 반환하는 고차 함수
// 이 함수가 반환하는 함수는 클로저로서 카운트 상태를 유지하기 위한 자유 변수 counter을 기억한다.
function makeCounter(predicate) {
  // 카운트 상태를 유지하기 위한 자유 변수
  var counter = 0;
  // 클로저를 반환
  return function () {
    counter = predicate(counter);
    return counter;
  };
}

// 보조 함수
function increase(n) {
  return ++n;
}

// 보조 함수
function decrease(n) {
  return --n;
}

// 함수로 함수를 생성한다.
// makeCounter 함수는 보조 함수를 인자로 전달받아 함수를 반환한다
const increaser = makeCounter(increase);
console.log(increaser()); // 1
console.log(increaser()); // 2

// increaser 함수와는 별개의 독립된 렉시컬 환경을 갖기 때문에 카운터 상태가 연동하지 않는다.
const decreaser = makeCounter(decrease);
console.log(decreaser()); // -1
console.log(decreaser()); // -2

정보의 은닉

function Counter() {
  // 카운트를 유지하기 위한 자유 변수
  var counter = 0;

  // 클로저
  this.increase = function () {
    return ++counter;
  };

  // 클로저
  this.decrease = function () {
    return --counter;
  };
}

const counter = new Counter();

console.log(counter.increase()); // 1
console.log(counter.decrease()); // 0
반응형

'개인공부 > 프론트엔드' 카테고리의 다른 글

TypeScript: typeAlias vs interface  (0) 2021.06.27
TypeScript: 제너릭  (0) 2021.06.27
JavaScript push를 이용해서 렌더링하기.  (0) 2021.06.09
XML HttpRequest ajax  (0) 2021.06.06
React : Hook (2018v)  (0) 2021.05.24