TIL 56 day 5장 클로저
클로저(Closer)
- 어떤 함수에서 선언한 변수를 참조하는 내부함수에서만 발생하는 현상
- 외부 함수의 LexicalEnvironment가 가비지 컬렉팅되지 않는 현상
- 어떤 함수 A에서 선언한 변수 a를 참조하는 내부함수 B를 외부로 전달할 경우 A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상
var outer = function () {
var a = 1;
var inner = function() {
return ++a;
};
return inner;
}
var outer2 = outer();
console.log(outer2()); // 2
console.log(outer2()); // 3
- outer 함수가 inner()를 return 했을 경우 inner 함수는 끝나면서 inner 함수의 실행컨텍스트가 종료되었을 것이다.
- 위의 예시에서는 outer 함수가 inner 함수를 return 하고 있다.
- outer2가 실행되면 outer 함수가 반환하는 inner함수가 실행된다.
- inner 함수 실행 시점에 outer 함수가 실행 종료가 되었는데 어떻게 outer 함수의 LexicalEnvironment에 접근 할 수 있을까?
=> 가비지 컬렉터는 어떤 값을 참조하는 변수가 하나라도 있으면 그 값은 수집대상에 포함시키지 않는다.
=> inner 함수의 실행컨텍스트가 활성화 된다면 outerEnvironmentReference가 outer 함수의 LexicalEnvironment를 필요로 할 것이므로 수집대상에서 제외
클로저와 메모리 관리
- 메모리 누수 : 개발자의 의도와 달리 어떤 값의 참조 카운트가 0이 되지 않아 GC의 수거 대상이 되지 않는 경우
- 참조카운트를 0으로 만드는 방법 : 일반적으로 기본형 데이터인 null이나 undefined 할당
var outer = (function () {
var a = 1;
var inner = function() {
return ++a;
};
return inner;
})();
console.log(outer());
console.log(outer());
outer = null; // outer 식별자의 inner 함수 참조를 끊음
클로저 활용
1) 콜백 함수 내부에서 외부 데이터 사용하고자 할 때
var outer = function () {
var a = 1;
var inner = function() {
return ++a;
};
return inner;
}
var outer2 = outer();
console.log(outer2()); // 2
console.log(outer2()); // 3
=> 가비지 컬렉터는 어떤 값을 참조하는 변수가 하나라도 있으면 그 값은 수집대상에 포함시키지 않는다.
=> inner 함수의 실행컨텍스트가 활성화 된다면 outerEnvironmentReference가 outer 함수의 LexicalEnvironment를 필요로 할 것이므로 수집대상에서 제외
var outer = (function () {
var a = 1;
var inner = function() {
return ++a;
};
return inner;
})();
console.log(outer());
console.log(outer());
outer = null; // outer 식별자의 inner 함수 참조를 끊음
<방법>
1. 콜백 함수를 내부함수로 선언해서 외부변수를 직접 참조하는 방법
2. bind 메서드로 값을 직접 넘겨 클로저는 발생하지 않으나 제약사항이 있는 방법
3. 콜백함수를 고차함수로 바꿔 클로저를 적극 활용한 방법
// 콜백함수를 고차함수로 바꿔 클로저 활용 방법 예시
var fruits = ['apple', 'banana', 'peach'];
var $ul = document.createElement('ul');
var alertFruitBuilder = function(fruit) {
return function() {
alert('your choice is ' + fruit);
};
};
fruits.forEach(function(fruit) {
var $li = document.createElement('li');
$li.innerText = fruit;
$li.addEventListener('click', alertFruitBuilder(fruit));
$ul.appendChild($li);
});
document.body.appendChild($ul);
2) 접근 제한 제어(정보 은닉)
- 정보은닉(information hiding) : 어떤 모듈의 내부 로직에 대해 외부로의 노출을 최소화해서 모듈간의 결합도를 낮추고 유연성을 높이고자 하는 현대 프로그래밍 언어의 중요한 개념
- JavsScript는 클로저를 사용해 함수 차원의 public한 값과 private한 값 구분 가능
- return을 사용한 방법 : 클로저 활용으로 외부 스코프에서 함수 내부의 변수들 중 선택적으로 일부의 변수에 대한 접근 권한을 부여
var outer = function() {
var a = 1;
var inner = function() {
return ++a;
};
return inner;
};
var outer2 = outer();
console.log(outer2());
console.log(outer2());
=> return 값이 외부에 정볼르 제공하는 유일한 수단
- 외부에 제공하고자 하는 정보를 모아선 return : 공개 멤버(public member)
- 내부에서만 사용할 정보는 return하지 않는다. : 비공개 멤버(private member)
=> 접근 권한 제어
3) 부분 적용 함수
- 부분 적용 함수(partially applied function) : n개의 인자를 받는 함수에 미리 m개의 인자만 넘겨 기억시켰다가 나중에 n-m개의 인자를 넘기면 비로소 원래 함수의 실행 결과를 얻을 수 있는 함수
- this 바인딩이 필요한 점을 제외하면 bind 메서드의 실행 결과가 부분 적용 함수
- 디바운스(debounce) : 짧은 시간 동안 동인한 이벤트가 많이 발생할 경우 이를 전부 처리하지 않고 처음 또는 마지막에 발생한 이벤트에 대해 한 번만 처리하는 것 => 성능최적화에 도움
var debounce = function(eventName, func, wait) {
var timeoutId = null;
return function(event) {
var self = this;
console.log(eventName, 'event 발생');
clearTimeout(timeoutId);
timeoutId = setTimeout(func.bind(self, event), wait)
};
};
var moveHandler = function(e) {
console.log('move event 처리');
};
var wheelHandler = function(e) {
console.log('wheel event 처리');
};
document.body.addEventListener('mousemove', debounce('move', moveHandler, 500))
document.body.addEventListener('mousewheel', debounce('move', wheelHandler, 500))
4) 커링 함수
- 커링함수(currying function) : 여러 개의 인자를 받는 함수를 하나의 인자만 받는 함수로 나눠서 순차적으로 호출될 수 있게 체인 형태로 구성한 것
- 커링은 한 번에 하나의 인자만 전달하는 것을 원칙
- 마지막 인자가 전달되기 전까지는 원본 함수가 실행 되지 않는다.
var curry5 = function (func) {
return function(a) {
return function(b) {
return function (c) {
return function (d) {
return function (e) {
return func(a, b, c, d, e);
}
}
}
}
}
}
var getMax = curry5(Math.max);
console.log(getMax(1)(2)(3)(4)(5)); // 5
var curry6 = func => a => b => c => d => e => func(a,b,c,d,e);
getMax2 = curry6(Math.max);
console.log(getMax2(1)(2)(3)(4)(5)); // 5
- 화살표 함수를 사용하면 한 줄로 표시 가능
- 각 단계에서 받은 인자들을 모두 마지막 단계에서 참조
=> GC되지 않고 메모리에 차곡차곡 쌓였다가 마지막 호출로 실행 컨텍스트가 종료된 후에야 비로소 한꺼번에 GC 수거대상이 됨 - 마지막 인자가 넘어 갈 때까지 함수 실행을 미룬다 => 지연실행(lazy execution)
var getInformation = function(baseUrl) {
return function(path) {
return function(id) {
return fetch(baseUrl + path + '/' + id);
};
};
};
// ES6
var getInformation = baseUrl => path => id => fetch(baseUrl + path + '/' + id);
- 일반적으로 공통적 요소는 먼저 기억시키고 특정값인 id만으로 서버 요청을 수행하는 함수! => 개발 효율성이나 가독성 측면에서 더 좋다!
Author And Source
이 문제에 관하여(TIL 56 day 5장 클로저), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@winney_77/TIL-56-day-클로저저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)