All Articles

JavaScript Closure

MDN에서는 클로저를 이렇게 정의를 한다.

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time. — MDN web docs

번역을 하면

클로저는 함수들과 그 함수들을 참조하는 스테이트들의 조합이다. 클로저 내부 함수의 scope에서 외부 함수의 scope에 접근할 수 있게 해준다. JavaScript에서는 함수가 생성될 때마다 함수 생성 시점에 클로저가 생성된다. — MDN web docs

클로저를 보기 전에 lexical environment를 봐야 합니다.

function init() {
  var name = 'Mozilla'; // name is a local variable created by init
  function displayName() { // displayName() is the inner function, a closure
    alert(name); // use variable declared in the parent function
  }
  displayName();
}
init();

위 예시는 MDN 에서 가져온 예시입니다. 함수 init 안의 name이라는걸 정의 하고 displayName이라는 함수를 또 정의합니다. 그 정의된 함수는 바깥 함수인 init에서 정의된 name을 써서 alert 창으로 띄웁니다. 결국엔 init를 실행하면 Mozilla가 출력됩니다. 위에 말했듯이 안의 함수는 바깥 함수의 것들을 불러와서 쓸 수 있습니다. 이게 바로 lexical environment입니다.

closure는 이 lexical scope를 재사용한다고 생각하면 편합니다.

function makeFunc() {
  var name = 'Mozilla';
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();

위 예제는 myFunc를 새로 정의해서 makeFunc의 기능처럼 새로 만듭니다. 그래서 새로 정의된 함수에서 값들을 바꾸면 기존 건 바뀌지 않습니다. 예를 들어서

function makeFunc(sentName) {
  var name = sentName;
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc1 = makeFunc();
var myFunc2 = makeFunc();
myFunc1('newName');
myFunc2('newName2');

이렇게 하면 alert 창으로 myFunc1는 ‘newName’, myFunc2는 ‘newName2’ 출력합니다.

조금 더 복잡한 예시를 보자면

function makeFunc(sentName) {
  return displayName(sentName2) {
    return sentName+sentName2;
  }
}

var myFunc1 = makeFunc('hello');
var myFunc2 = makeFunc('hi');
console.log(myFunc1('Gi')); //helloGi
console.log(myFunc1('aaasd'));//helloaaasd
console.log(myFunc2('Bong'));//hiBong
console.log(myFunc2('123124'));//hi123124

이렇게 하면 myFunc1, myFunc2는 옆에 주석대로 출력합니다.

closure를 쓰는 이유 중 또 하나의 이유는 private 메서드를 이미테이션 하기 위해서이다.

var counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  };
})();

console.log(counter.value()); // logs 0
counter.increment();
counter.increment();
console.log(counter.value()); // logs 2
counter.decrement();
console.log(counter.value()); // logs 1

위는 counter에 private라고(외부에서 접근이 불가능하다고 가정하고) 치면 우리는 counter 안의 함수인 increment와 decrement나 value로 counter의 데이터를 가져오거나 수정을 한다.

var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }
};

var counter1 = makeCounter();
var counter2 = makeCounter();
alert(counter1.value()); /* Alerts 0 */
counter1.increment();
counter1.increment();
alert(counter1.value()); /* Alerts 2 */
counter1.decrement();
alert(counter1.value()); /* Alerts 1 */
alert(counter2.value()); /* Alerts 0 */

더 나아 가서 아예 새로 만들어서 쓸 수도 있다. 당연히 새로 만들었으니 세 counter는 개별적으로 관리가 된다.

위에서 봤듯이 closure를 쓰므로 함수를 새로 작성을 안 하고 재활용도 가능하고 데이터 관리를 할 수 있고 private인 데이터들을 git객체 안에 함수들을 넣어서 원하는 조건으로만 변경을 제한 할 수 있습니다. 이러한 장점이 있음으로 closure를 씁니다.

reference:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures https://poiemaweb.com/js-closure https://velog.io/@ki_blank/Javascript-%ED%81%B4%EB%A1%9C%EC%A0%80Closure