Closure 2

Closure 2

코어 자바스크립트(정재남 저) 책을 기본으로 하여 자바스크립트를 공부하면서 내용을 정리하고자 한다.


05 Closure

🏌🏼‍♂️ Closure와 메모리


앞서 Closure 현상이 일어나는 이유는 GC(garbage Collector)의 동작 방식이라고 얘기했었다.

Closure를 통해 내부 변수를 참조하는 동안에는 내부 변수가 차지하는 메모리를 GC가 회수하지 않기 때문에 과도한 메모리 소모가 일어날 수 있다.

사용하지 않는 Closure는 다음과 같은 방법으로 참조를 제거하는 것이 좋다.

🤹🏼‍♂️ 메모리 관리의 예

👉🏼 For Example 1

closure2_1

👉🏼 For Example 2

closure2_2

👉🏼 For Example 3

closure2_3


필요하지 않는 Closure의 참조 카운트를 0으로 만들어주면 된다.

🤾🏼‍♂️ 방법은 식별자에 null 값을 할당하여 참조를 끊는다.



🏂 Closure의 활용

🪃 상태를 유지한다.

Closure의 핵심 포인트는 현재 상태를 기억하고 변경된 최신 상태를 유지하는데 유용하다는 점이다.

👉🏼 For Example 1

closure2_4

위의 코드에서 보면 즉시실행함수를 통해 반환되는 toggleClosure를 생성한다.

Closure 내부에서 익명함수 리턴시 변수 isShow는 상위 스코프의 변수를 참조한다.

이벤트리스너의 콜백함수로 전달된 toggle은 버튼이 클릭되면 호출되는데, 이때 내부의 변수 isShow이 변경된다.

변경된 변수 isShow의 상태는 Closure에 의해 지속적으로 참조되기 때문에 현재 상태가 유지된다.

버튼이 클릭될 때마다 현재 상태를 유지한 변수는 변경된 후 최신 상태를 유지한다.


쉽게 말해서 isShow 변수는 클릭될 때마다 true, false 의 값으로 바뀌는데, 전 상태의 값을 유지하기 때문에 클릭될 때마다 켜졌다 꺼졌다 할 수 있는 것이다.


👉🏼 For Example 2

closure2_5

위에서 본 예시코드이다.

최신 상태를 유지한다는 말은 callMeByName 함수가 참조하고 있는 변수인 times가 Closure 내부에서 지속적으로 참조되고 있기 때문에 현재 값이 계속 유지되는 것을 뜻한다.

그렇기에 callMeByName2가 호출될 때마다 times 변수의 값을 기억하고 있다가 숫자를 증가시키는 것이다.

이는 굳이 전역변수를 사용하지 않고, 최신 상태를 유지할 수 있다.



🪃 전역 변수의 Side Effect 위험 방지

전역 변수 사용시 일어날 수 있는 Side Effect를 방지할 수 있다.


A side effect refers simply to the modification of some kind of state

- Changing the value of a variable;

👉🏼 For Example

closure2_6

위와 같이 name, times 변수를 전역변수로 설정해보자.

결과값은 마찬가지로 동일하다.


자, 여기서 한 번 변수의 값을 변경해보자.

closure2_7

전역 변수를 사용한 위의 코드에서 times 변수의 값을 10으로 재할당하였다.

callMeByName2를 호출해보니, 재할당된 값 10을 토대로 결과값이 변경됨을 볼 수 있다.

전역 변수를 사용하면 의도와 다르게 값이 변경될 위험의 소지가 있다.


자, 이번엔 Closure 에서 변경을 해보자.

closure2_8

goodGuy 함수 외부에서 times의 값을 10으로 할당해보았다. 그리고 callMeByName2 를 호출해보니, 영향을 받지 않는다.

Closure인 goodGuy 함수 내부의 변수인 times의 값은 변경되지 않는다. 함수 내부의 변수는 외부에서 접근이 불가한 변수이기 때문이다.

위에서 10을 할당한 times 변수는 선언은 되지 않았지만, 값이 할당된 goodGuy 함수 내부의 변수인 times와는 다른 변수이다.

Closure 내부의 변수의 값이 외부에서 변경되지 않는 특징 때문에 상태를 안전하게 계속 유지할 수 있다.


Closure를 사용하면 전역 변수를 사용하지 않고도 최신의 상태를 유지하면서, 값이 변경될 위험에서도 벗어날 수 있다.



🪃 정보의 은닉

👉🏼 For Example 1

closure2_9

생성자 함수 Counter는 increase와 decrease, 2개의 메소드를 가지는 인스턴스를 생성하는 함수다.

이 2개의 메소드는 Counter의 스코프에 속한 변수 counter를 기억하는 Closure이다.

생성자 함수 Counter 내부에서 생성된 변수 count는 increase와 decrease가 실행되면, 두 메소드가 상위 스코프의 변수를 참조할 수 있는 Closure이기 때문에 접근이 가능하지만, 외부에서 직접 접근하려고 하면 접근이 불가능하다.

이처럼 Closure는 변수를 은닉화해서 접근 불가능하게 만드는 기능을 한다. 또는 캡슐화라고도 한다.


👉🏼 For Example 2

closure2_10

직원의 리스트를 만드는 클래스를 리턴하는 함수 createEmployee가 있다.

employeeId 변수는 외부에서는 직접 접근이 불가하지만, createEmployee 함수가 리턴하는 내부 함수에서는 참조가 가능하기에 새롭게 생성한 angelina, julia에는 id가 생성되는 것을 볼 수 있다.


closure2_11

employeeId 변수에 아무거나 할당해보고, 다시 새로운 인스턴스를 생성하면

변수에는 영향이 없기 때문에 현재 상태에서 1이 추가된 id값 3의 ann 인스턴스가 생성된다.



🏉 혼동하기 쉬운 Closure

👉🏼 For Example

closure2_12

georgeID.id()이 실행될 때 i의 값이 무엇인가가 중요한 점이다. 여기서 i는 전역변수이기 떄문에 값에 영향을 미친다.


이런 실수를 고치는 방법은 2가지가 있다.


🗝 첫번째 방법은 IIFE를 사용하는 방법이다.

closure2_13


🗝 두번째 방법은 let을 사용하는 방법이다.

closure2_14

var 키워드가 i를 전역 변수로 만드는 것을 막기 위해 let 키워드를 사용하면 된다.


코드 참고하려면



참고자료