on
클로저(closure)
클로저란?
사전적 의미
closure : 닫힘, 폐쇄, 완결성
MDN에서의 정의
: Lexical envirnment(내부 환경정보)와 내부함수와의 조합으로 함수가 생성될 때 매번 함께 발생한다.
즉, 함수의 생성과 함께 무조건 생기는 당연한 개념이다.
하지만 실제로는 클로저를 보편적인 상황에 모두 적용하지는 않고 클로저를 활용한, 클로저 환경에서만 발생하는 무언가 특별한 현상을 표현하기 위해 클로저라는 단어를 사용한다.
실행 컨텍스트의 내부 식별자 정보와 해당 실행 컨텍스트의 내부 함수에서의 외부 식별자 정보는 서로 관련이 있고 위에서 말한 이 둘사이의 조합은 이 두가지 뿐이다.
- 실행 컨텍스트의 environmentRecord와
- 내부 함수의 outerEnvironmentReference
그래서 다시 정리하면 Closure는 실행 컨텍스트에서 선언한 변수를 내부함수에서 참조할 때 발생하는 특별한 현상이다.
특별한 현상이란?
var outer = function(){
var a= 1;
var inner = function(){
console.log(++a);
}
inner();
}
outer();
여기서 outer()는 전역 컨텍스트의 내부 식별자 정보에서의 outer()값을 참조하고 a와 inner에 대한 내부 식별자 정보를 갖는 다
inner()는 outer()의 내부 식별자 정보에서의 inner()값을 참조하고 내부 식별자 정보는 없다.
여기까지는 특별한 현상이랄게 없다.
var outer = function(){
var a= 1;
var inner = function(){
return ++a;
}
return inner;
}
var outer2 = outer();
console.log(outer2());
console.log(outer2());
inner()
안에서 console.log
를 하지 않고 ++a
를 return
해주고 outer()
안에서 inner
를 실행하지 않고 return
시켰다.
그리고 outer()
의 결과를 outer2
라는 변수에 담았다.
각 실행 컨텍스트를 살펴보자.
- 전역 실행 컨텍스트에
outer : f, outer2 : undefined
로 내부 식별자 정보가 정의된다. outer()
의 실행 컨텍스트가 실행되어 진행되면서outer()
실행 컨텍스트에는 외부 식별자 정보로 전역 컨택스트의outer()
값을 참조하고 내부 식별자 정보에 변수a
의 값이1
로 할당되고innter()가
정의 된다.outer()
결과로inner
값이 반환되니까 전역 컨텍스트의outer2: undefined
가outer2: inner
로 변경된다.outer()
실행 컨텍스트는 종료가 되어 사라지지만 전역 컨텍스트에 outer2: inner
가 정의되어 있어서 언제든outer2
로 인해서inner()
가 호출될 수 있기 때문에inner
내부에서 참조하고 있는outer()
컨텍스트 내부 식별자 정보에 기록된a:1
이 죽지 않고 살아있다. 즉outer()
실행 컨텍스트는 종료가 되었지만outer
의 내부 식별자 정보 중a
가 살아있다. 원래는 끝나서 사라지는 게 맞지만 참조 카운트가 0이라서 사라지지 못하고 살아있는 좀비 상태가 된 것이다.- 이 상태에서
outer2()
가 실행되면서inner()
실행 컨텍스트가 생성된다. inner()
컨텍스트 내부에서a
에 접근을 하는데 이때a
는 종료가 된outer()
를 참조하는 외부 식별자 정보에 담겨있다.7.여기에 있는
a
즉 , 종료된outer()
실행 컨텍스트의 내부 식별자 정보a
로 접근해서a
의 값을 변경하게 된다. 그리고 반환을 하게 되면inner()
컨텍스트가 종료가 되고console.log(outer2());
가2
로 출력된다. 다시 또console.log(outer2());
이 실행되면서inner()
가 실행되고a
는3
이 되서3
이 출력이 되고 전역 컨텍스트가 종료되고 그때서야outer()
의a
도 사라진다.
이러한 현상이 위에서 정의한 “컨텍스트에서 선언한 변수를 내부함수에서 참조할 경우에 발생하는 특별한 현상”에서의 특별한 현상이다.
클로저의 활용
클로저는 컨텍스트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 laura = user('Laura');
user()
는 _logged: true
라는 값과 객체를 리턴하고 있다.
이 객체 안에는 user()
의 매개변수인 _name
을 받는 다. _name
은 user()
의 environment(내부 식별자 정보)에 정의되고 이 객체(user()
가 return
하는 객체)는 outerEnvironmentReference(외부 식별자 정보)로 _name
을 참조하는 형태이다.
객체 안에서 get name()
안에서 _name
을 리턴하고 있기 때문에 클로저로 발생하여 user()
실행 컨텍스트가 종료됨과 동시에 사라질 매개변수 _name
이 리턴할 객체 안에서 또 리턴되면서 사용되고 있어서 참조카운트가 0이 안되니까 나중에 쓰일 변수라고 판단해서 계속 살아있게 된다.
_logged
도 마찬가지이다.
그래서 laura.name
혹은 laura.status
로 내부 변수에 접근할 수 있다.
또 name
에 대한 getter
와 setter
가 설정되어 있어서 name
의 값을 변경시킬 수 있지만 status
는 getter
만 설정되어 있기 때문에 status
의 값을 직접 바꾸지는 못한다.
ex) laura.name = ‘jjoo’; (ok) laura.status = false; (no)
Reference
- 인프런 코어 자바스크립트
Discussion and feedback