클로져를 이해하기 위해선 스코프에 대해 이해하고 있어야 한다.
변수 이름, 함수 이름 ,클래스 이름과 같은 식별자가
본인이 선언된 위치에 따라 다른 코드에서 자신이 참조될 수 있을지 없을지 결정되는것
코드가 전역에서 선언된다면 전역변수, 지역에서 선언된다면 지역변수가 된다.
어떤 변수를 참조해야하는지를 알고 싶으면 스코프체인
을 알아야 한다.
함수 내부에서 함수가 또 정의될 수 있는데 이를 중첩함수라고하고, 중첩합수를 포함하는 함수를 외부함수라고 한다. 이렇듯 스코프는 함수의 중첩에 의해 계층적인 구조를 가질 수가 있는데 이것을 스코프체인이라고 한다.
변수를 참조할때 자바스크립트 엔진은 스코프체인을 통해 변수를 참조한다.
현재 함수가 어떤 변수를 참조하려고 하는데 내 스코프안에 원하는 함수가 없으면 상위스코프로 올라가고 없으면 또 위로 올라가면 최상위 전역 스코프까지 올라가게되는데, 여기서도 원하는 변수가 없으면 ReferenceError(참조에러)를 출력한다.
상위스코프가 결정되는 시점을 기준으로 동적스코프와 정적스코프로 나뉘는데,
함수가 호출되는 시점에 결정되는 것이 동적스코프이고 함수가 정의되는 시점에 결정
되는것이 정적스코프(===렉시컬 스코프)
이다.
자바스크립트는 렉스컬스코프를 따르기 때문에 함수가 정의되자마자 상위스코프가가 결정이 되고, 이후에 해당 함수에 의해 함수객체가 생성이 되면 해당 함수 객체는 본인의 상위 스코프를 항상 알게 될 수 있게 된다.
자바스크립트 함수는 태어나면 자신의 내부 슬롯에 상위스코프의 참조를 저장하기 때문이다.
함수는 다음과 같은 흐름으로 진행이 되는데,
먼저 호출된 함수에 실행 컨텍스트를 생성
하고 이를 실행 컨텍스트 스택
에 Push하고, 그 후에 함수는 본인의 렉시컬환경
을 생성한다.
(( 렉시컬환경은 함수 본인 내부의 식별자, 식별자에 바인딩 된 값 등을 기록하고 있는 하나의 자료구조이다.))
마지막으로 코드의 실행이 끝나면 실행컨텍스트 스택에서 해당 컨텍스트를 pop하여 제거한다.
이 코드를 보자면
const x = 1;
function outer(){
const x = 10;
const inner = function(){
console.log(x);
};
return inner;
}
const result = outer();
result();
outer함수는 중첩함수 inner를 result에게 반환하면서 생명주기를 마감한다.
즉 outer함수의 호출이 종료되면 outer함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 제거가 된다. 따라서 outer함수의 지역변수 x또한 생명주기를 동시에 마감하게 되고 더이상 지역변수x에 접근할 방법이 없어보인다..
하지만❗️
코드를 실행하면 10이라는 값이 나오는데 죽은x가 어떻게 다시 살아 돌아왔을까?
여기서 클로져
에 대한 개념이 나온다.
중첩함수 inner가 이미 생명주기를 마감한 outer함수의 지역변수x를 참조할 수 있다면 이 때 inner를 클로져
라고 한다.
outer함수가 result에게 inner함수를 반환하고 사라지고, result는 inner함수 객체를 참조하고 inner함수 객체는 본인의 내부 슬롯에 저장된 상위스코프에 의존하여 outer함수의 렉시컬 환경을 참조하기 때문에 가비지컬렉션의 대상이 되지 않는다.
((실행 컨텍스트 스택에서 outer함수는 제거가 되었지만 outer함수의 렉시컬 환경까지는 소멸되지 않음))
따라서 result에 의해 inner함수를 호출하면 outer함수안에 있는 변수x를 다시 참조할 수 있게 되는 것이다.
중첩함수가 상위 스코프의 식별자를 참조하고 있고,
본인의 외부 함수보다 더 오래 살아있다면 클로져
라고 하며,
이렇게 클로져에 의해 참조된 변수를 자유변수
라고 한다.
클로져
는 하나의 state가 의도치 않게 변동되지 않도록 안전하게 은닉하고, 특정 함수에게만 state변경을 허용하기 위해 사용하는 것이다. ☻
출처:
https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures
https://www.youtube.com/watch?v=tpl2oXQkGZs