클로저(Closure)
- MDN says: A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). ... In Javascript, closures are created every time a function is created, at function creation time.
- 내부 함수와 LexicalEnvironment의 조합
- <=> 실행 컨텍스트 A 내에서 함수 B를 생성한 경우, A(실행 컨텍스트)의 lexical environment와 내부함수 B의 조합에서 나타나는 특별한 현상
- <=> 컨텍스트 A에서 선언한 변수를 내부함수 B에서 참조할 경우에 발생하는 특별한 현상
var outer = function() { var a = 1; var inner = function() { console.log(++a); } inner(); } outer();
- 위 예제에서 클로저를 사용해 특별한 현상이라고 확인할 수 있는 부분은 딱히 없다.
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함수 호출될 때 call stack
- outer 함수는 return 된 것이 없고, 아직 실행 중이기에 outer2 변수는 undefined.
- outer 함수가 종료된 이후 (outer 함수가 종료되었지만 아래와 같이 outer 컨텍스트 내에 a라는 변수는 여전히 유효한 상태.)
- a 라는 변수는 inner 함수에서 사용되고, inner 함수는 outer2에서 참조하고 있는 상황.
- 곧 a의 참조 카운트가 0이 아닌 상황.
- 첫번째 outer2 함수가 호출 되었을 때
- 첫번째 outer2 함수가 종료 되었을 때
- 두번째 outer2 함수가 호출 되었을 때
클로저의 핵심
- 컨텍스트 A에서 선언한 변수 a를 참조하는 내부함수 B를 A의 외부로 전달할 경우, A가 종료된 이후에도 a가 사라지지 않는 현상
- 지역변수가 함수 종료 후에도 사라지지 않게 할 수 있다.
- <=> 함수 종료 후에도 사라지지 않는 지역변수를 만들 수 있다.
클로저의 진가를 느낄 수 있는 예제
function user(_name) {
var _logged = true;
return {
get name() { return _name },
set name(v) { _name = v; },
login() { _logged = true },
logout() { _logged = false },
get status() {
return _logged ? 'login' : 'logout';
},
}
}
var roy = user('재남');
console.log(roy.name);
// 재남 출력, roy객체에는 name 속성이 없음에도 값이 출력되는 이유는 name에 대한 getter가 있어 getter가 호출이 되기 때문이다.
// getter안에 _name 변수는 user 함수를 호출할 때 매개변수로 넘겨받은 값을 갖고 있는, user함수에서 선언된 변수이다. 원래는 user 함수가 종료될 때, 같이 사라졌어야할 변수이지만, user 함수가 return하는 부분에 _name이란 변수를 사용하는 곳이 있기에(곧 참조 카운트가 0이 아닌 상태이기에) _name이 유효하게 된다.
// 함수는 죽었지만, 변수는 끈질기게 살아남은 상황. 그렇기에 '재남'이 출력되는 것이다.
roy.name = '제이' // name에 대한 setter가 호출된다.
console.log(roy.name) // '제이' 출력
roy._name = '케이' // roy 객체 안에 _name이란 프로퍼티가 존재하지 않는다.
console.log(roy.name) // '제이' 출력, name getter에 대한 호출 결과는 동일하게 '제이'
console.log(roy.status) // status 에 대한 getter 호출되어 'login' 출력
roy.logout();
console.log(roy.status) // logout 출력
roy.status = true; // status에 대한 setter가 없기에 해당 명령은 무시된다.
console.log(roy.status) // 위 명령은 무시되기에 여전히 logout 출력
위 예제를 통해 아래의 내용을 확인할 수 있다.
- 함수 종료 후에도 사라지지 않고 값을 유지하는 변수(_name, _logged)
- 외부로부터 내부 변수 보호(캡슐화)
- _logged라는 변수는 login, logout 함수를 통해서만 수정 가능
Todo
- 클로저에 의한 메모리 누수를 관리하는 방법
- 클로저를 활용한 다양한 프로그래밍 기법
'javascript' 카테고리의 다른 글
[CoreJavascript] 6과 프로토타입(Prototype) (0) | 2025.03.05 |
---|---|
[CoreJavascript] 4과 콜백함수(callback function) (0) | 2025.02.13 |
[CoreJavascript] 3과 this (0) | 2025.01.30 |
[CoreJavascript] 2과 실행컨텍스트 (0) | 2025.01.28 |
[CoreJavascript] 1과 데이터 타입 (0) | 2025.01.24 |