Closures
Closures는 자바스크립트가 제공하는 막강한 추상 개념이다. 하지만 동시에 혼란스럽기도 하다. 직역하면, 폐포(닫힌 주머니)를 의미한다.
1 2 3 4 5 | function makeAdder(a) { return function(b) { return a+b; } } | cs |
x = makeAdder(5);
x(6)
-> 11
makeAdder 함수는 한 매개변수를 통해 호출되고, 주어진 매개변수를 더하는 새 익명함수를 생성한다.
또 다른 외부 함수 혹은 호출자가 외부 함수의 변수에 액세스한 다는 점에서 내장함수가 수행하는 일과 비슷하다고 볼 수 있다.
한가지 다른점은 외부 함수가 리턴된다는 점인데, 일반적으로 생각할 때 변수는 사라진다고 볼 수 있으나 여전히 존재하고 있다.
또한, 자바스크립트의 함수가 실행될 때는 언제나, 스코프 객체가 생성되어 해당 함수내에 생성된 지역변수를 저장하고 있다.
함수 매개변수로서 넘어온 어떤 값이라도 초기값으로 저장하고 있다. 이것은 전역변수 및 전역 객체와 비슷하지만, 2가지 차이점이 있다.
1) 함수가 실행될 때마다 새로운 스코프 객체가 생성된다.
2) 전역 객체와 달리 스코프 객체는 자바스크립트 코드에서 직접 접근 불가능하다.
따라서 makeAdder 함수가 호출되면, 스코프 객체는 makeAdder 함수에 매개변수로 넘겨진 하나의 속성 값 a를 갖는 상태로 생성된다.
일반적으로 자바스크립트의 가비지 컬렉터가 makeAdder에 의해 생성된 범위 객체를 제거해야하지만, 리턴된 함수가 여전히 스코프 객체를 참조한다.
결과적으로 스코프 객체는 makeAdder에 의해 리턴된 함수가 더는 참조되지 않을 때까지 가비지컬렉터는 삭제하지 않는다.
또한 Closures는 상태를 저장하도록 허용한다. 그렇기 때문에 객체 내부에서 자주 사용될 수 있는 것이다.
즉 Closures는 함수와 함수에 의해 생성되는 범위 객체를 함께 지칭하는 용어이다.
메모리 누출
자바스크립트는 객체가 생성됨에 따라 메모리를 할당하고, 더 참조하는 다른 객체가 없으면 메모리에서 제거하는 등 가비지컬렉트를 하는 언어이다.
이에 따라, Closures의 부작용은 IE에서 심각하지는 않지만 메모리 누출이 쉽게 된다는 것이다.
브라우저 호스트는 HTML 페이지의 DOM 객체로 표현된 많은 수의 객체를 다루어야 한다.
객체들을 어떻게 할당하고 거둬들이는 지는 브라우저의 책임이다. IE는 자신만의 고유한, 자바스크립트와는 다른 가비지 컬렉션 방식을 사용한다.
IE에서 메모리 누출은 자바스크립트 객체와 고유 객체간의 참조하는 중 자기 자신을 참조(circular reference : 순환 참조)하게 되는 일이 발생할 경우라면 언제든지 발생하게 된다.
1 2 3 4 5 6 7 | function leakMemory(){ var el = document.getElementById('el'); var o = {'el' : el }; el.o = o; } | cs |
위의 코드는 순한 참조로서 메모리 누출을 일으킨다. IE는 완전히 다시 시작하기 전까지 el과 O에 의해 사용되는 메모리를 반환하지 못한다.
일반적으로 메모리 누출이 이렇게 명확한 경우는 드물다. 누출을 일으키는 데이터 구조는 수차례에 거친 참조 구조를 가지고 있어 순환참조를 하고 있는지 명확하지 않기 때문이다.
Closures는 위와 같은 경우가 아니더라도 간단하게 메모리 누출이 일어날 수 있다.
1 2 3 4 5 6 7 | function addHandler(){ var el = document.getElementById('el'); el.onclick = function(){ this.style.backgroundColor = 'red'; } } | cs |
위의 코드는 클릭했을 때 배경색이 바뀌는 엘리먼트를 설정한다. 그리고 메모리 누출 또한 일어난다.
el을 참조하면 의도와 달리 익명함수 때문에, 생성된 Closures 내에 객체가 붙잡혀 있기 때문이다.
이는 자바스크립트 객체(내부 함수)와 원시 객체(el) 간의 순환 참조를 만든다.
다음과 같은 방법을 통해 이 문제를 피할 수 있다.
1 2 3 4 5 6 7 | function addHandler(){ var el = document.getElementById('el'); el.onclick = function(){ this.style.backgroundColor = 'red'; } el = null; } |
혹은 다음과 같은 방법으로 메모리 누출을 피할 수 있다.
1 2 3 4 5 6 7 8 9 10 | function addHandler(){ var clickHandler = function(){ this.style.backgroundColor = 'red'; } (function(){ var el = document.getElementById('el'); el.onclick = clickHandler; })(); } | cs |
클로져에 의해 발생된 순환 참조를 끈기 위해 또 다른 클로져를 추가 했다.
내부함수는 실행되고 바로 사라지므로 clickHandler와 함께 생성된 Closure로 부터 내용을 숨긴다.
'programming > JavaScript' 카테고리의 다른 글
사용자 정의 객체, 프로토타입, call, apply (0) | 2018.05.27 |
---|---|
배열, 함수 (0) | 2018.05.26 |
객체 (Object) (0) | 2018.05.26 |
데이터 타입, 변수, 연산자 (0) | 2018.05.26 |