Language/JavaScript

자바스크립트 클로저

SambaLim 2019. 8. 8. 23:05
자바스크립트 클로져

유인동님의 함수형 자바스크립트 프로그래밍을 시작하며

자바스크립트의 함수와 관련된 몇 가지 기능들에 대해 알아보고자 합니다.

 

클로져

클로져는 자신이 생성될 때의 환경을 기억하는 함수이다. 이 문장만 보면 클로져가 너무 어렵다고 생각될 수가 있습니다. 따라서 조금 더 쉽게 표현하고자 예시를 들어보려 합니다.

 

예시

클로져는 자신이 생성될 때의 스코프에서 알 수 있었던 변수를 기억하는 함수다.

그렇다면, 자바스크립트의 모든 함수는 클로져냐는 질문이 가능합니다. 이는 해석하는 관점에 따라 다르지만 클로져의 조건에 하나를 더 달아 사용하도록 하겠습니다.

상단의 예시를 통해 보면 innerFunc내에서 사용하는 변수 중 innerFunc내에서 선언되지 않은 변수(outerVar)가 있어야 합니다.

이러한 특성을가지고 클로져는 두 가지 이유에서 보통 사용됩니다.

  1. Side Effects를 관리하기위해
  2. Create Private Variables

 

Controlling side effects with closures

Ajax, Timeout등을 사용할 때, console.log를 사용하는 것도 Side Effect를 야기합니다. 이와 같은 것들은 코드의 흐름을 방해할 수 있는데 이를 Closure를 통해 제어할 수 있습니다. 예시를 통해 알아보도록 합시다.

 

예시

보시는 것과 같이 makeCake함수는 Side Effect를 갖습니다. setTimeout을 사용했기 때문입니다. 이제 makeCake함수에 flavor을 추가해보도록 하겠습니다.

makeCake를 실행해보도록 하겠습니다.

실행하였을 때, 1초가 지난 즉시 Made a banana cake!라고 출력됩니다.

우리는 flavor을 알자마자 Made a banana cake!를 출력하는 것이 아닌, flavor을 알게된 후에, 원하는 시점에 Made a banana cake!를 출력하고 싶다 생각하고 새로운 함수 prepareCake를 추가합니다.

prepareCake함수 내부에서는 클로져인 makeCake를 반환합니다.

makeCakeLater을 사용하여 원하는 시기에 함수를 호출할 수 있고, 1초 뒤, Made a banana cake!를 출력할 것입니다.

이와 같이 클로져를 사용해서 사이드이펙트를 방지할 수 있습니다.

(내부 클로져를 호출할 수 있는 함수를 만듬)

 

Closures and scope

클로져는 자신이 생성될 때의 스코프에서 알 수 있었던 변수 중 언젠가 자신이 실행될 때 사용할 변수들만 기억하여 유지시키는 함수다.

 

예시

func1 에서 ab를 참조하여 실행결과가 30으로 나온다.

그렇다면, func1은 클로져일까? 유인동님의 함수형 자바스크립트 프로그래밍 책에 의하면 아니다!

우선 func1이 클로져로 오해받을 수 있는 원인은 Func1의 스코프 상위에 있는 변수 ab를 참조한다는 것이다.

하지만 abfunc1에 의해 사라지지 못하는 상황이 아니다. ab는 글로벌 스코프에서 선언되었고 다른 하위 함수에 참조될 수는 있지만 사라지지는 못한다.

 

예시

func3을 클로져라고 할 수 있을까? 유인동님의 대답은 역시 No다.

앞의 예시와 다르게 func3func2스코프 안에서 생성되었고, c, dfunc3에서 정의되어 사용하고있다.

하지만 자신이 생성될 때의 스코프가 알고 있는 변수 a, b는 사용하지 않았으므로 func3이 생성될 때, 기억하고 있는 변수는 없다. 따라서 클로저가 아니다.

 

예시

위 예시에는 드디어 클로져가 있을까? 이번에도... No다. 하지만 정확한 표현은 있었다.이다.

func4가 실행된 후, ab가 할당된다. 그리고 func5ab를 사용해서 return값을 출력하므로 func5는 자신이 생성된 환경을 기억하고 있었다고 할 수 있다.

 

예시

감격스럽게도 func7은 클로져이다.

func7a를 사용하기에 a를 기억해야하고 func7func8에 담겼기 때문에 클로져가 되었다. func6를 실행하였을 때, func6의 지역변수는 모두 사라져야 하지만 func6이 끝난 이후에더 func7func6a를 기억하는 클로져가 되었기 때문에 a는 사라지지 않는다.

따라서 func8을 실행할 때마다 a는 새로운 변수 b와 함께 사용되어 결과를 만든다.

 

Closure and Memory Leak

클로져가 될까봐 개발자들이 함수안에서 보조함수들을 선언하기 꺼려하였었습니다.

이는 함수안에서 함수를 정의하면 클로져가 된다. 라고 생각했기 때문이었습니다.

클로져가 된다면 특정 변수(위의 예시에서는 a)를 기억해야하고 이는 메모리 누수를 야기한다고 생각했기 때문입니다.

하지만 우선 Closure and scope의 세 번째 예시처럼 함수내부에 함수를 정의하여 외부변수를 참조하는 것만으로 클로져가 되지 않습니다.

네 번째 예시처럼 클로져가 되더라도 afunc8을 실행할 때, 한 번 생겨날 뿐, 계속해서 생기거나 하지 않습니다. (어떻게 이렇게 되는지 알고싶으시면 사파리, 크롬등의 최적화를 알아보시면 됩니다.)

 

Closure and timing

func11(30)의 결과는 놀랍게도 60이 출력된다. 이는 클로져는 자신이 생성될 때의 스코프에서 알 수 있었던 변수를 기억하는함수이기 때문이다. 여기서 는 생각보다 길다.

클로져는 자신이 생성되는 스코프의 모든 라인, 어느 곳에서 선언된 변수든지 참조하고 기억할 수 있다.

클로져는 자신이 생성되는 스코프의 실행 컨텍스트에서 만들어졌거나 알 수 있었던 변수 중 언젠가 자신이 실행될 때 사용할 변수들만 기억하는 함수이다. 클로져가 기억하는 변수의 값은 언제든지 남이나 자신에 의해 변경될 수 있다.

'Language > JavaScript' 카테고리의 다른 글

자바스크립트 Array (배열)  (0) 2019.09.18
자바스크립트 고차함수  (0) 2019.08.27
자바스크립트 스코프  (0) 2019.08.03
자바스크립트 일급함수  (0) 2019.08.02
자바스크립트의 프로토타입  (0) 2019.05.24