// closure.html 파일
<!DOCTYPE html>
<html lang="ko">
<head>
<title>클로저 실습</title>
<script>
function aaa(){
const apple = 10
function bbb(){
console.log(apple)
}
bbb()
}
aaa();
</script>
</head>
<body>
클로저 실습
</body>
</html>
bbb함수 스코프 안에 apple이라는 변수가 없어 aaa라는 상위함수의 스코프로 찾아 올라가 apple 이라는 변수를 찾게 됩니다.
aaa 함수는 bbb의 closure가 되며 bbb가 apple을 찾아 올라갈 수 있는 이유는 우리가 이전에 배웠던 실행컨텍스트가 외부 환경 요소를 수집하기 때문입니다.
즉, 클로저라고 함은 상위 함수와, 해당함수(여기서는 bbb함수)가 선언된 스코프 즉 상위함수를 둘러싼 환경이 되겠습니다.
bbb함수에서 aaa함수 스코프로 올라가는 과정에서 스코프 체인이 일어난다고 보시면 됩니다.
스코프 체인과 클로저 간단 정리
- 클로저(closure) : 상위 함수 + 상위함수의 lexical enviroment(상위함수를 둘러싼 환경)
- 스코프 체인(scope chain) : 바로위 함수 스코프 뿐만아니라 global 스코프 까지 찾아 올라가는 과정을 scope chain이라고 합니다.
const x = 1;
function outer() {
const x = 10;
const inner = function() { console.log(x)};
return inner;
}
// outer 함수를 호출하면 중첩 함수 inner를 반환한다.
// 그리고 outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 팝되어 제거된다.
const innerFunc = outer();
innerFunc();
outer 함수를 호출하면 outer 함수는 중첩 함수 inner를 반환하고 생명주기를 마감한다. 즉, outer 함수의 호출이 종료하면 이 함수의 실행 컨텍스트는 스택에서 제거된다.
그래서 외부의 x 는 1이어야 하는데 결과값은 10이다.
이처럼 외부함수보다 중첩함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명주기가 종료한 외부 함수의 변수를 참조할 수 있다. 이러한 중첩 함수를 클로저라고 부른다.
outer 함수의 실행 컨텍스트는 스택에서 제거되지만 outer 함수의 렉시컬 환경까지 소멸되는것은 아니기 때문에 outer 함수의 렉시컬 환경은 inner 함수의 내부슬롯에 의해 참조되고 있고 inner 함수는 전역변수 innerFunc에 의해 참조되고 있으므로 가비지 컬렉션의 대상이 되지 않기 때문이라고합니다!
가비지 컬렉터는 누군가가 참조하고 있는 메모리 공간을 함부로 해제하지 않습니다.
MDN 정의에 따르면 클로저는 함수와 그 함수가 선언된 렉시컬 환경
과의 조합이다. 라고 되어있네요
❓ 렉시컬 환경??
외부 렉시컬 환경을 참조하는 과정
렉시컬 환경이 만들어질 때 Environment라는 숨김 프로퍼티가 만들어진다. 프로퍼티에는 외부렉시컬 환경이 저장되어있으며 지역내에서 참조할 값이 없을 경우 프로퍼티에 접근하여 외부 렉시컬 환경을 참조하게되는데 값이 없을 경우 최상위 렉시컬 환경까지 검색하게 되는 체이닝 과정이 일어난다. 그렇기 때문에 모든함수는 클로저라고 할 수 있다.
자바스크립트의 모든 함수는 상위 스코프를 기억하므로 이론적으로 모든 함수는 클로저가 됩니다.
하지만 일반적으로 모든 함수를 클로저라고 하지는 않는다고 하네요.