본문 바로가기
자바스크립트/Javascript

javasciript closure(클로저)란??

by 디찌s 2021. 1. 7.
728x90
반응형

클로저를 이해하려면 자바스크립트가 어떤 변수의 유효범위를 지정하는지 (Lexical scoping)을 먼저 이해 해야한다.

 


1.Lexical scoping(어휘적 범위 지정)

아래 코드를 보자

function init() {
  var name = "Chrome"; // name은 init에 의해 생성된 지역 변수이다.
  function displayName() { // displayName() 은 내부 함수이며, 클로저다.
    alert(name); // 부모 함수에서 선언된 변수를 사용한다.
  }
  displayName();
}
init();

init()함수는 지역변수 name과 displayName()을 생성한다. displayName()은 init()안에 정의된 내부 함수이며 init()함수 본문에서만 사용할수있다.

여기서 알아두어야 할것은 displayName 함수에는 init()함수에 name변수처럼 지역변수가 따로 없다는것이다.

그런데 함수 내부에서 외부함수의 변수에 접근할수 있기에 displayName()역시 부모함수 init()에서 선언된 변수 name에 접근할수있다.

만약에  displayName()이 자신만의 name변수를 가지고 있다면 name대신 this.name을 사용하여 한다.

 

 

function init() {
  var name = "Chrome"; // name은 init에 의해 생성된 지역 변수이다.
  function displayName() { // displayName() 은 내부 함수이며, 클로저다.
    alert(name); // 부모 함수에서 선언된 변수를 사용한다.
  }
  displayName();
}
init();

위 코드를 실행하면 alert문을 통해 name의 값이 성공적으로 출력된다.

이 예시를 보면 함수가 중첩된 상황에서 parser가 어떻게 변수를 처리하는지 알수있다.

이는 어휘적 범위 지정(lexical scoping)의 한예이다.

 

2.클로저(Closure)

 

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

var myFunc = makeFunc();
//myFunc변수에 displayName을 리턴함
//유효범위의 어휘적 환경을 유지
myFunc();
//리턴된 displayName 함수를 실행(name 변수에 접근

위에 코드는 바로 전의 예제와 완전 동일한 결과가 실행된다. 하지만 차이는 displayName()함수가 실행되기전에 외부함수인 makeFuncz()로부터 리턴되어 myFunc변수에 저장된다는것이다.

 

여러 프로그램언어를 사용한 사람들은 이 코드가 작동하지 않는다고 볼수도있다.

하지만 자바스크립트는 잘작동한다. 그이유는 자바스크립트는 함수를 리턴하고, 리턴하는 함수가 클로저를 형성하기 때문이다.

 

클로저는 함수와 함수가 선언된 어휘적 환경의 조합이며, 이 환경은 생성된 시점의 유효 범위내에 있는 모든 지역변수로 구성된다.

 

첫 번째 예시의 경우, myFunc는 makeFunc가 실행될때 생성된 displayName함수의 인스턴스에 대한 참조이다.

displayName의 인서턴스는 변수 name이 있는 어휘적 환경에 대한 참조를 유지하며, 이러한 이유로 myFunc가 호출될 때 변수 name은 사용할 수 있는 상태로 남게되고 "Mozilla"가 alert에 전달된다.

 

function makeAdder(x) {
  var y = 1;
  return function(z) {
    y = 100;
    return x + y + z;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);
//클로저에 x와 y의 환경이 저장됨

console.log(add5(2));  // 107 (x:5 + y:100 + z:2)
console.log(add10(2)); // 112 (x:10 + y:100 + z:2)
//함수 실행 시 클로저에 저장된 x, y값에 접근하여 값을 계산

 

위 예제는 단일 인자 x를 받아 새함수를 반환하는 함수 makeAdder(x)를 정의한다.

반환되는 함수는 단일 인자 z를 받아 x와y와z의 합을 반환한다.

 

본질적으로 makeAdder은 함수를 만들어내는 공장이다. 이는 makeAdder함수가 특정한 값을 인자로 가질수있는 함수들을 리턴한다는것을 의미한다.

 

위의 예제에서 add5,add10 두개 의 새로운 함수들을 만들기 위해 makeAdder 함수 공장을 사용했다. 하나는 매개변수 x에 5를 더하고 다른 하나는 매개변수 x에 10을 더한다.

 

add5와 add10은 둘 다 클로저이다. 이들은 같은 함수 본문 정의를 공유하지만 서로 다른 맥락적 환경을 저장한다.

함수 실행 시 add5의 맥락적 환경에서 클로저 내부의 x는 5이며, add10의 x는 10이다.

 

1. 실용적인 클로저

클로저는 어떠한 데이터와 그 데이터를 조작하는 함수를 연관시켜주기에 유용하다.

 

이것은 객체가 어떤 데이터와(그 객체의 속성) 하나 혹은 그 이상의 메소드들을 연관시킨다는 점에서 객체지향 프로그래밍과 분명히 같은 맥락에있다.

 

결론적으로 오직 하나의 메소드를 가지고 있는 객체를 일반적으로 사용하는 모든 곳에 클로저를 사용할수있다.

 

이렇게 할 수 있는 상황은 특히 웹에서 일반적이다. 프론트 엔드 자바스크립트에서 우리가 쓰는 많은 코드가 이벤트 기반이다.

 

우리는 몇 가지 동작을 정의한 다음 사용자에 의한 이벤트에 연결한다. 우리의 코드는 일반적으로 콜백으로 첨부된다.

 

2.클로저를 이용 프라이빗 메소드 흉내내기

자바스크립트는 클로저를 이용하여 프라이빗 메소드를 흉내낼수있다.

프라이빗 메소드는 코드에 제한적인 접근만을 허용한다는 점 뿐 만아니라, 전역 네임 스페이스를 관리하는 강력한 방법을 제공하여, 불필요한 메소드가 공용 인터페이스를 혼란스럽게 만들지 않도록한다.

 

아래 코드는 프라이빗 함수와 변수에 접근하는 퍼블릭 함수를 정의하기 위해 클로저를 사용하는 방법을 보여준다

이렇게 클로저를 사용하는것을 모듈패턴이라한다.

 

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

 

 

 

728x90
반응형

댓글