클로저는 함수와 선언된 어휘적 환경의 조합으로 함수와 그 함수가 선언될 당시의 lexical environment의 combination이다.
→ 새로운 현상이 나오는 것→ 실행 콘텍스트 A의 내부에서 함수 B를 선언한 상황
→ 실행 콘텍스트 A와 함수 B가 콤비가 되어 무언가를 한다.
Lexical Environment에는 아래 2가지가 있다.
- environmentRecord
- outerEnvironmentReference
B 입장에서 실행 콘텍스트 A와 함수 B에 무언가가 있다는 것은 B Lexical Environment가 가지고 있는 outerEnvironmentReference만 A와 B사이에 관여한다.
→ B의 outerEnvironmentReference는 A의 environmentRecord를 참조한다.
클로저란?
→ 콘텍스트 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()); console.log(outer2());
- 전역 객체가 열리고 outer와 outer2가 environmentRecord에 수집된다.
- outer 함수의 실행 콘텍스트가 열리면 a, inner 함수가 environmentRecord에 수집된다.
- outer의 실행 콘텍스트가 끝나면 outer2에 inner함수가 담겨있다.
outer의 environmentRecord의 a는 1로 되어있는 상태
→ outer 함수에 있는 a, outer의 실행 콘텍스트는 종료됐지만
참조 카운터가 아직 살아있는 상태이다.- outer2를 실행하면 outer에 있는 a를 2로 바꾼다.
- outer2의 실행이 정료되면 콘솔에 2가 찍힌다.
- 다시 outer2를 호출하고 실행이 종료된 뒤 콘솔에 3이 찍힌다.
→ 위의 실행코드를 살펴보면, 전역 콘텍스트가 남아있는 한
참조 카운트는 계속 살아있는 것을 확인할 수 있다.만약, outer2에 다른 값을 넣게되면 a에 대한 참조 카운트가 없어지게 될 것이다.
컨텍스트 A에서 선언한 변수를 내부 함수 B에서 접근할 경우에만 발생하는 특수한 현상이다.
컨텍스트 A에서 선언한 변수 a를 참조하는 내부함수 B를 A의 외부로 전달할 경우,
A가 종료된 이후에도 a가 사라지지 않는 현상을 말한다.
var outer = function () { var a = 1; var inner = function () { return ++a; } return inner; } var outer2 = outer(); console.log(outer2()); console.log(outer2());
→ 함수 종료 후에도 사라지지 않는 지역변수를 만들 수 있다.
→ 위의 코드처럼 outer 함수를 호출하면 a의 값만 얻어오면 되는 것이기 때문에 outer 함수 전체를 다 만들 필요가 없다.
그렇기 때문에 outer 함수는 한 번만 만들어놓고, 나머지 값은 필요없고, 필요한 값인 a 한 개만 계속해서 활용하고 싶을 때 클로저를 사용할 수 있다.
초기화 세팅 용도로 클로저를 만들고 계속 들고 사용해야 할 값들만 리턴시키고, 나머지는 초기화 함수가 종료시킬 때 없애고, 필요한 것만을 사용할 때 좋다.
→ 초기화 할 때 좋다고한다.
→ 선택적으로 할 수 있고, 원하는 값을 임의로 외부로 꺼낼 수 있다는 것이 클로저의 장점이다.
아래 코드를 통해 클로저의 장점을 살펴 볼 것이다.
function a () { var localA = 1; var localB = 2; var lacalC = 3; return { get a() { return localA }, set a(v) { localA = v; }, get b() { return lacalB + localC; }, set b(v) { throw Error('read only'); } } } var obj = a();
위의 코드를 살펴보면 get/setter를 묶은 객체를 리턴하고 있다.
→ obj.a 를 할 경우 localA의 값을 리턴한다.
→ obj.a = 3;을 하면 set에 의해 localA의 값을 바꾼다.
- 객체에 값에 접근해서 값을 읽고, 변경할 수 있다.
- localA에 직접 접근하는 것이 아니라 지역변수를 보호하면서도 접근하는 수단을 외부로 뺀것
get b를 살펴보면 b의 값을 읽는 것이 아니라 완전 다른 값인
b+c
가 계산된 값을 내보낸다.
→ 값을 변경할 수 있고, 보호할 수도 있다.
→ 내부에서 권한을 정해놓고 외부에서는 내부에서 정한 권한만 ⇒ 클로저가 구현한 권한만 이용할 수 있다.