Closure (클로저) , 어휘적 환경

태로샐러드·2021년 7월 22일
0

javascript 기초

목록 보기
13/22
post-thumbnail

🍫 클로저란?

  • 함수와 함수가 선언된 어휘적(lexical) 환경의 조합.

이런 정의만 보면 직관적으로 이해가 잘 가지 않는다..
어휘적(lexical) 환경은 단순하게는 변수 및 함수 선언의 형태라고 일단 이해하자.
하지만, 클로저 함수를 보다보면 약간 어떤 느낌인지는 알 것 같다.

🍫 어휘적(lexical) 환경

  • 클로저를 이해하기 위해 필수적인 개념이다.
let name = "rotae"

function myName() {
  let name = "taero"
  function displayMyName () {
    return name;    // "taero"
  }
}

위 코드에서 displayMyName 함수는 리턴값으로 taero를 출력한다.
1번줄의 name이 아닌 myName 함수의 name 값을 출력하는 이유는 지역변수가 우선하기 때문이다.
즉, displayMyName 함수의 name 변수는 myName의 name 변수를 참조하는 것이다.
myName 함수 범위 밖에 아무리 name 변수가 새로 선언되어도 변하지 않는다.
어휘적(lexcial) 환경은 바로 이것이다.
myName함수의 name 변수는 변하지 않는 특정한 어휘적 환경을 따르고 있다.

🍫 클로저함수

  • 함수를 리턴하는 함수의 형태여야 한다.
  • 내부함수가 외부함수의 변수에 접근할 수 있어야 한다.
const adder = function (x) {
  return function(y) {
    return x + y;
  }
}

const add5 = adder(5);
const add10 = adder(10);

일반적인 함수는, 함수 실행이 종료되면 함수 내부의 변수를 사용할 수 없다.
하지만 위 예시의 add5를 보면 adder 함수에서 x에 5라는 값을 받아 계속 유지시키고 있다.
즉, adder라는 외부함수가 종료되었는데도 adder의 x라는 변수에 5라는 값이 메모리에 저장되는 것이다.
따라서, add5 라는 새롭게 탄생한 함수로

add5(7);   // 12
add5(10);  // 15
add10(7);  // 17
add10(10); // 20

이와 같은 계산을 할 수 있는 것이다.

여기서 짚고 넘어가야 할 것은 add5는 add10과 같은 함수 본문을 공유하지만,
서로 다른 어휘적(lexical) 환경을 저장한다는 점이다.
add5의 어휘적(lexical) 환경에서의 x는 5를 나타내고
add10의 어휘적(lexical) 환경에서의 x는 10을 나타낸다.

🍫 우리는 이것을 모듈화 라고 부르기로 했어요.

위에서 클로저 함수의 특징을 잘 활용하면,
내부 함수를 단 하나만 리턴하지 않고, 객체에 담아 여러 기능을 하는 다수의 내부 함수를 리턴할 수 있다.
이것을 클로저 모듈 패턴 이라고 부른다.

const makeCounter = () => {
  let value = 0;
  
  return {
    increase: () => {
      value = value + 1;
    }
    decrease: () => {
      value = value - 1;
    }
    getValue: () => {
      value;
    }
  }
}

const counter1 = makeCounter();

보자보자, 어디보자.
우선 makeCounter 라는 함수 내에
value 가 0으로 선언되어 있고, 객체 하나를 리턴하는데
그 객체의 key는 각각 increase, decrease, getValue 로 되어있고
그 key에 대응하는 value 는 함수로 되어있다.

그 다음, makeCounter()의 값을 counter1이라는 새로운 변수에 담게 되면
counter1의 값은 객체인
{ (increase : 함수1), (decrease : 함수2), (getValue : 함수3)} 이 된다.

값을 1 더해주는 increase(),
값을 1 빼주는 decrease(),
누적값을 보여주는 getValue()
따라서 원본 makeCounter() 외부함수를 통해, 이 세가지 모두에 접근할 수 있는 것이다!

객체기 때문에 아래 처럼 dot notation 혹은 bracket notation 을 통해,
각 함수(기능)에 접근 하면 된다.

cont counter1 = makeCounter();
counter1.increase();  // value값 +1
counter1.increase();  // value값 +1
counter1.decrease();  // value값 -1
counter1.getValue();  // vlaue값 출력 = 1

🍫 클로저의 장단점

돌아와서 클로즈 함수의 핵심은
외부함수의 스코프, 즉, 함수가 선언된 어휘적 환경에 접근할 수 있다는 점이다.

장점

  • 커링(currying, 함수 하나가 n개의 인자를 받는 대신 n개의 함수를 만들어 각각의 인자를 받는 방법)과 클로저 모듈(변수를 외부 함수 스코프 안쪽에 감추어, 변수가 함수 밖에서 노출되는 것을 막는 방법) 등의 패턴을 구현할 수 있다.

단점

  • 일반 함수는 함수 실행 종료 후 가비지 컬렉션 대상이 되었을 객체가, 클로저 패턴에서는 메모리 상에 남아있게 된다. 외부함수 스코프가 내부함수에 의해 언제든지 참조될 수 있기 때문이다. 그래서 클로저를 남발하면 퍼포먼스 저하가 발생할 수도 있다.
profile
기획, 개발공부, 그 외 잡다한 여정 기록 (SEMI로)

0개의 댓글