[자바스크립트] 클로져(Closure)

2020. 2. 29. 17:20프로그래밍-Web/Javascript

클로져는 자바스크립트 ES6에서 let과 const를 지원하면서부터

거의 사용성이 소멸된 개념이지만

그래도 이해의 관점에서는 중요하기 때문에 정리해보았다.

 

1) 스코프

 

스코프를 한줄로 정의하면 '변수의 유효범위'가 된다.

즉, 특정 변수가 존재할 때, 이것이 영향을 미치는 범위를 뜻하는 것이다.

이를 다시한번 가공시켜 보자면, 변수의 선언을 어디에서 했는지 파악하는 것이라고 풀어낼 수 있다.

 

예시 코드를 보자.

이게 무슨 당연한 소리야! 라고 할 수도 있겠지만, a를 찍어내는 함수와 b를 찍어내는 함수의 기재는 다르다

 

a를 찍어내는 과정을 생각해보자면,

전역컨텍스트에서 a를 정의한 후-> aPrint 컨텍스트에서 찍어낼 a를 탐색-> global에서 정의된 a를 찾았다! 

 

하지만 b를 찍어내는 과정은

전역컨텍스트에서 b를 정의한 후-> bPrint 컨텍스트에서 다시한번 b를 정의 -> bPrint 컨텍스트에서 찍어낼 b를 탐색-> bPrint안에서 정의된 b를 찾았다! 

 

요 둘의 차이는, b의 경우엔 bPrint안에서 한 번 더 정의되었기 때문에, global에서 정의된 b는 garbage가 된다는 것이다.

 

이렇게 당연하고 쉬운 설명을 굳이 늘어놓은 것은

let,const와 var의 차이를 이해하기 위함이다.

 

var의 경우엔 '함수 스코프'에 해당된다. 그래서 특정 var이 재선언되기까지는 global하게 변수 스코프가 적용된다.

그러나 let과 const의 경우는 '블록 스코프'에 해당된다. 직역하자면 블록안에서만 스코프가 적용된다는 뜻과 같다.

그렇기 때문에 특정 블록에서 빠져나온다면, 해당 선언은 약속은 파기된다.

 

예시로 설명해보자면, a의 경우는 3이 출력되고 b의 경우는 아예 에러가 난다.

b는 선언조차 되지 않았기 때문이다.(만약 let b;를 선언해준다면 undefined이 된다)

 

즉, var은 함수가 끝날때까지 해당 선언이 계속 적용되고, let의 경우에는 중괄호({})로 묶여진 부분만 스코프로 가지는 것이다.

비슷한 예를 하나 더 들어보자.

global에서 var a와 var b를 모두 선언하였다.

하지만 let의 경우엔 p2블록만 스코프로 갖게 되므로 let b; 만 반영하여 undefined이 출력 될 것이고

var의 경우엔 p1이 실행된 global 스코프를 갖게 되므로, 새롭게 할당된 3이 출력된다.

 

특히 이 부분은 클로져와 for문의 결합에 대한 이해에 도움이 된다.

 

또한 var은 값의 재선언, 재할당이 모두 가능하고, let의 경우는 값의 재할당은 가능하지만

const의 경우엔 값조차 재할당이 불가하다.

JAVA의 final과 유사하다.

 

 

2) 클로져

 

클로져는 '함수와 함수가 선언된 어휘적 환경의 조합' 이다.

많은 개발자들이 이에 대해 의역하길

'함수 밖에서 선언된 변수를 함수 내부에서 사용할때 작동하는 상태'라고.

이를 다시 내 식대로 바꿔보자면 클로져는

'내부 함수 사용을 위한 가상공간'이라고 할 수 있겠다. 

흠흠.

 

전형적인 클로져의 예시 코드를 보자

해당 함수를 실행하면 gihong이라는 글자가 출력된다.

이 과정을 이해해보자.

일단 우리는 innerF라는 함수에 inner function만을 리턴해주었다.

메모리상에서의 그림을 보자면

요런 상태

그리고 inner를 리턴해주게 되면 outer 메모리는 제 몫을 하고선 소멸해버린다

그래서 우리가 innerF 함수를 사용하고 싶지만, name에 대한 메모리가 소멸됐기 때문에 사용이 불가능하게 된다.

 

하지만 실제로 사용이 불가능하느냐?

그렇지않다.

왜냐하면 innerF를 밖에 선언하는 순간 클로져를 만들어

얘가 inner가 생성될 당시 참조한 외부 변수를 고스란히 기억해낸다.

그렇기때문에 우리는 언제나 innerF를 사용할 수 있다.

(단, inner()의 경우엔 애초에 innerF로 리턴해주었기 때문에 클로져에 담겨져있지 않아도 된다.) 

 

이를 보통 그대로 사용하지 않고, JAVA이 Getter, Setter처럼 캡슐화를 시켜 사용한다

 

저렇게 setInfo라는 녀석과 getGoral이라는 녀석을 리턴 메소드로 정하고

해당 객체에 접근할떄에는 각각 사용할 수 있도록 정의해준다.

이를 메모리 관점에서 보자면 다음과 같다

ㅇㅋ?