자신이 선언될 당시의 환경을 기억하는 함수
"함수 안에 함수를 선언한 환경에서의 관계" 를 의미
함수안에 함수를 선언한 환경은 내부 함수에서 외부 함수로 지역변수를 접근할 수 있지만 외부 함수의 실행이 끝나고 외부 함수가 소멸된 이후에도 내부 함수가 외부 함수의 변수에 접근할 수 있는 것을 의미합니다.
이게 어떻게 가능하냐면!
💡 자바스크립트는 함수를 리턴하고 리턴되는 함수가 클로저를 형성하기 때문에 접근이 가능합니다.
✴️ 클로저는 반환된 내부함수가 자신이 선언되었을때의 환경(Lexical Environment)에서의 스코프를 기억하기 때문에 접근이 가능합니다.✴️
const closureComponent = () => {
// 외부 함수 영역
const outerFunc = () => {
let x = 1000;
// 내부 함수 영역
let innerFunc = () => {
console.log(x);
// 내부 함수에서 외부 함수로 지역 변수의 접근을 한다.
};
return innerFunc;
};
// 외부 함수를 실행합니다.
const callInnerFunc = outerFunc();
// 외부 함수의 실행이 끝나더라도 변수에 접근이 가능 합니다.
callInnerFunc(); // 1000
};
export default closureComponent;
💡 Private Method 란?
import { useEffect } from 'react';
/**
* 클로저를 이용한 Private Method 구현하기
* @returns
*/
const closure1PrivateMethod = () => {
useEffect(() => {
console.log(counter.value()); // logs 0
counter.increment();
counter.increment();
console.log(counter.value()); // logs 2
counter.decrement();
console.log(counter.value()); // logs 1
}, []);
/**
* 숫자를 늘리고 줄이는 함수
*/
const counter = (() => {
// 외부의 함수의 변수를 선언합니다.
let count = 0;
// 전달 받은 값을
const changeBy = (value: number) => {
count += value;
};
return {
// 값을 증가합니다.
increment: (): void => {
return changeBy(1);
},
// 값을 줄입니다.
decrement: () => {
return changeBy(-1);
},
// 값을 출력합니다.
value: () => {
console.log(count);
return count;
},
};
})();
return <div></div>;
};
export default closure1PrivateMethod;
import { useState } from 'react';
const closure2React = () => {
const [count, setCount] = useState(0);
const onClickBtnHandler = () => {
setCount(count + 1);
};
return (
<div>
<div>
<button type='button' onClick={onClickBtnHandler}>
클로저 테스트
</button>
</div>
<div>
<input type='text' value={count} />
</div>
</div>
);
};
export default closure2React;
import { useEffect } from 'react';
const closure3Modlues = () => {
useEffect(() => {
counter.increment();
counter.increment();
counter.increment();
console.log(counter.value()); // 3
counter.decrement();
counter.decrement();
counter.decrement();
console.log(counter.value()); // 0
}, []);
/**
* 숫자를 세는 클로저를 이용한 모듈
* @returns
*/
const counter = (() => {
let count = 0;
return {
// 값을 증가합니다.
increment: () => {
return (count += 1);
},
// 값을 줄입니다.
decrement: () => {
return (count -= 1);
},
// 값을 출력합니다.
value: () => {
return count;
},
};
})();
return (
<></>
);
};
export default closure3Modlues;
function makeAdder(x) {
var y = 1;
return function(z) {
y = 100;
return x + y + z;
};
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
//클로저에 x와 y의 환경이 저장됨
console.log(add5(2)); // 107 (x:5 + y:100 + z:2)
console.log(add10(2)); // 112 (x:10 + y:100 + z:2)
//함수 실행 시 클로저에 저장된 x, y값에 접근하여 값을 계산
이 예제에서 단일 인자 x를 받아서 새 함수를 반환하는 함수 makeAdder(x)를 정의했다. 반환되는 함수는 단일인자 z를 받아서 x와 y와 z의 합을 반환한다.
본질적으로 makeAdder는 함수를 만들어내는 공장이다. 이는 makeAdder함수가 특정한 값을 인자로 가질 수 있는 함수들을 리턴한다는 것을 의미한다. 위의 예제에서 add5, add10 두 개의 새로운 함수들을 만들기 위해 makeAdder함수 공장을 사용했다. 하나는 매개변수 x에 5를 더하고 다른 하나는 매개변수 x에 10을 더한다.
add5와 add10은 둘 다 클로저이다. 이들은 같은 함수 본문 정의를 공유하지만 서로 다른 맥락(어휘)적 환경을 저장한다. 함수 실행 시 add5의 맥락적 환경에서 클로저 내부의 x는 5 이지만 add10의 맥락적 환경에서 x는 10이다. 또한 리턴되는 함수에서 초기값이 1로 할당된 y에 접근하여 y값을 100으로 변경한 것을 볼 수 있다. (물론 x값도 동일하게 변경 가능하다.)
이는 클로저가 "리턴된 후에도 외부함수의 변수들에 접근 가능"하다는 것을 보여주는 포인트이며 클로저에 단순히 값 형태로 전달되는것이 아니라는 것을 의미한다.