클로저

최형필·2021년 2월 10일

코어자바스크립트

목록 보기
5/6

https://media.vlpt.us/images/corete/post/a497abc4-c54e-470b-9033-987516342a62/%EC%BD%94%EC%9E%90%201%EC%9E%A5.png

클로저의 의미 및 원리 이해


  • 클로저란 어떤 함수 A에서 선언한 변수 a를 참조하는 내부함수 B를 외부로 전달할 경우 A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상
var outer = function () {
  var a = 1;                // a 변수를 inner가 참조하고 있으므로 가비지 컬렉팅에서 제외됨
  var inner = function () {
    return ++a;
  }
  return inner; // inner 함수 자체를 반환
};
var outer2 = outer();
console.log(outer2()); // 2
console.log(outer2()); // 3

클로저와 메모리 관리


  • 클로저는 메모리 누수의 위험이 있다.
  • 개발자가 의도적으로 참조 카운트를 0이 되지 않게 설계한 경우는 누수가 아니다.
  • 필요에 의해 클로저를 사용했다면 사용이 끝난 후 식별자에 참조형이 아닌 기본형 데이터(null, undefined)를 할당하면 된다.

클로저 활용 사례


콜백 함수 내부에서 외부 데이터를 사용하고자 할 때

var fruit = ['apple' , 'banana', 'peach'];
var $ul = document.createElement('ul'); //(공통 코드)

fruits.forEach(function(fruit) { //(A)
  var $li = document.createElement('li');
  $li.innerText = fruit;
  $li.addEventListner('click', function() { //(B)
    alert('your choice is' + fruit);
  });
  $ul.appendChild($li);
});
document.body.appendChild($ul);
  • (B) 의 outerEnvironmentReference가 (A)의 LexicalEnvironment를 참조하므로 (B)함수가 참조할 예정인 변수 fruit에 대해서는 (A)가 종료된 이후에도 GC 대상에서 제외되어 계속 참조가 가능함
  • 하지만 B는 재사용을 할 수 없음
var alertFruitBuilder = function(fruit) {
	return function() {
		alert('your choice is ' + fruit);
	};
};

fruit.forEach(function(fruit) {
	var $li = document.createElement('li');
	$li.innerText = fruit;
	$li.addEventListener('click', alertFruitBuilder(fruit));
	$li.appendChild($li);
});
  • 고차 함수를 활용하면 alertFruitBuilder의 실행 결과로 반환된 함수에는 클로저가 존재한다.

접근 권한 제어 (정보 은닉)

  • 정보 은닉은 어떤 모듈의 내부 로직에 대해 외부로의 노출을 최소화해서 모듈간의 결합도를 낮추고 유연성을 높이고자 하는 현대 프로그래밍 언어의 중요한 개념
  • 클로저를 이용하면 함수 차원에서 public한 값과 private한 값을 구분하는 것이 가능하다. 외부 스코프에서 함수 내부 변수들 중 선택적으로 일부 변수에 대한 접근 권한을 부여할 수 있는데, 바로 return 을 활용하면 된다.

부분 적용 함수

  • 부분 적용 함수란 n개의 인자를 받는 함수에 미리 m개의 인자만 넘겨 기억시켰다가, 나중에 (n-m)개의 인자를 넘기면 비로소 원래 함수의 실행 결과를 얻을 수 있게끔 하는 함수
  • debounce등이 있다.

커링 함수

  • 커링 함수란 여러 개의 인자를 받는 함수를 하나의 인자만 받는 함수로 나눠서 순차적으로 호출될 수 있게 체인 형태로 구성한 것
  • 커링은 한번에 하나의 인자만 전달하는 것이 원칙
  • 중간 함수는 그 다음 인자를 받기 위해 대기만 할 뿐
  • 마지막 인자가 원본함수 실행
var getInformation = function (baseUrl) {
	return function (path) {
		return function (id) {
			return fetch(baseUrl + path + '/' + id);
		};
	};
};

var getInformation = baseUrl => path => id => fetch(baseUrl + path + '/' + id);
  • Flux 아키텍처중 하나인 Redux의 미들웨어가 커링으로 구현되어있다.
//Redux Middleware 'logger'
const logger = store => next => action =>{
	console.log('dispatching', action);
	console.log('next state', store.getState());
	return next(action);
};

//Redux Middleware 'thunk'
const thunk = store => next => action => {
	return typeof action === 'function'
			? action(dispatch, store.getState)
			: next(action);
};
  • 두 미들웨어는 공통적으로 store, next, action 순서로 인자를 받는다. 이중 store는 프로제그 내에서한번 생성된 이후로는 바뀌지 않는 속성이고 dispatch의 의미를 가지는 next 역시 마찬가지지만, action의 경우는 매번 달라진다.
  • store와 next 값이 결정되면 Redux 내부에서 logger 또는 thunk에 store, next를 미리 넘겨서 반환된 함수를 저장시켜 놓고, 이후에는 action만 받아서 처리할 수 있게끔 동작한다.
profile
프론트엔드 개발자

0개의 댓글