
모던 자바스크립트 Deep Dive 서적을 참고했습니다.
사실 클로저는 자바스크립트 고유의 개념이 아니다.
클로저의 정의가 ECMAScript사양에 등장하지 않는다.
MDN(신뢰할 수 있는 개발자 문서. 공식문서는 아님) 曰: “클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.”
하... 렉시컬은 또 무엇일까? 이왕 하는 김에 쉽고 빠르게 보고 오자. ⇒ 렉시컬 빠르게 집어넣기 클릭
렉시컬 스코프 개념을 이해했다면 술술 읽힐겁니다.
함수는 '태어난 곳'과 '일하는 곳'이 다를 수 있다. 그래서 함수는 '일하는 곳'과 관계없이 자신이 '태어난 곳'의 환경을 기억해야 하는데, 이때 '태어난 곳'(정의 된 위치, 환경)이 바로 상위 스코프이다. => 기억해야 한다.
다시! 상위 스코프란?: 함수의 정의가 위치하는 스코프
이를 위해 함수는 자신의 내부슬롯[[Environment]]에 자신이 정의된 환경, 즉 상위 스코프의 참조를 저장한다.
함수의 [[Environment]]가 어떻게 상위 스코프를 결정하는지 보면:
- 상위 스코프 결정 시점
 
- 함수 정의가 평가될 때(코드가 실행되기 전) 상위 스코프가 결정된다.
 
- 이것이 바로 '렉시컬 스코프'.
 - [[Environment]]의 역할
 
- 함수가 자신이 태어난(정의된) 환경을 기억하게 해준다.
 
자바스크립트 deep dive 24-04
const x = 1;
function foo() {
    const x = 10;
    bar();
}
function bar() {
    console.log(x);
}
bar함수는 전역에서 정의되었으므로 전역 스코프를 기억한다.- 그래서
 bar함수가 어디서 호출되든 상관없이 전역의x값인 1을 참조한다.foo안에서 호출되어도 마찬가지이다.
즉, 함수의 상위 스코프는 함수를 어디서 호출하는지가 아니라, 함수를 어디서 정의했는지에 따라 결정된다. 이것이 바로 렉시컬 스코프의 핵심이다.
이 코드하나 머리에 박아놓고 클로저하면 떠올리기
   function foo() {
       const x = 1;
       const y = 2;
   
       function bar() {
           console.log(x); // bar는 foo의 x에 접근 가능
       }
       return bar;
   }
   
   const bar = foo();  // foo를 실행하고 bar 함수를 반환받음
   bar();  // bar 실행
   
생명 주기 관점에서 보기
   function outer() {
       const name = "철수";  // 원래는 outer 함수 종료시 사라져야 할 변수
   
       function inner() {
           console.log(name);  // 하지만 inner가 name을 기억함
       }
       return inner;  // inner 함수 반환
   }
   
   const savedFunc = outer();  // outer는 실행 종료됨
   savedFunc();  // 여전히 "철수" 출력 가능!
   
- 보통의 경우: outer 함수가 끝나면 그 안의 변수들(name)은 사라져야 함
 - 클로저의 경우: inner 함수가 name을 참조하고 있어서 사라지지 않고 유지됨
 
여기에서 의문이 들 수도 있다! (의문 없다면 pass)
“엥?
outer()함수는 일급객체로써 변수savedFunc에 넣었기때문에savedFunc();를 실행하면outer();가 실행되니까 애초에 이 결과가 당연한거 아니야?” 라고 생각이 들었다면
그렇게 오해할 수 있지만 실제로는 다르게 작동한다.
     function outer() {
         const name = "박지성";
     
         function inner() {
             console.log(name);
         }
         return inner;  // inner 함수를 '반환'
     }
     
     // 여기서 잘 보면
     const savedFunc = outer();  // (1) outer 함수가 실행되고 inner 함수를 반환
     savedFunc();  // (2) 저장된 inner 함수를 실행
     
const savedFunc = outer()에서 일어나는 일:
savedFunc()에서 일어나는 일:
다시 보자면
 // 이렇게 생각이 들 수 있는데
 const savedFunc = outer;  // outer 함수 자체를 저장
 savedFunc();  // outer 함수를 실행
 
 // 실제로는 이렇게 동작함
 const savedFunc = outer();  // outer()의 반환값(inner 함수)을 저장
 savedFunc();  // 저장된 inner 함수를 실행
 
비유하자면
outer()는 "조리법"을 주는 게 아니라"일찍 소멸된다"는 의미
 function foo() {
     const x = 1;
     const y = 2;
 
     // 케이스 1: 클로저가 모든 변수 유지
     function bar() {
         console.log(x, y);  // x와 y 모두 필요
     }
 
     // 케이스 2: 최적화된 클로저
     function optimizedBar() {
         console.log(x);  // x만 필요
         // y는 사용하지 않아서 메모리에서 일찍 제거됨
     }
 }
 
이게이게 자바스크립트 엔진의 최적화를 도와준다.
- optimizedBar는 x만 사용하므로 y는 메모리에서 제거
 
- 불필요한 메모리 점유를 줄임
 
function counter() {
    let count = 0;  // 외부에서 직접 접근 불가능한 변수
    return {
        increase() { count++; },
        decrease() { count--; },
        getCount() { return count; }
    };
}
const myCounter = counter();
myCounter.increase();  // count는 private하게 보호됨
console.log(myCounter.getCount());  // 1
function bigFunction() {
    const bigData = new Array(10000);  // 큰 데이터
    const smallData = "hello";         // 작은 데이터
    return function() {
        console.log(smallData);  // smallData만 사용
        // bigData는 사용하지 않으므로 메모리에서 제거됨
    };
}
- 최적화 전: bigData와 smallData 모두 메모리 유지
 - 최적화 후: smallData만 메모리에 유지, bigData는 일찍 소멸
 
- 이렇게 클로저는 함수가 자신이 생성된 환경의 변수를 기억하되, 실제로 사용하는 변수만을 메모리에 유지하는 최적화된 방식으로 동작한다.
 
- 메모리 효율성 & 코드의 캡슐화를 동시 달성!
 
그래서 자바스크립트에서 클로저가 뭐냐고 물어보면 뭐라고 답해야하지..?!
렉시껄 스코프는 "함수를 어디에 선언했는지"에 따라 상위 스코프를 결정하는
규칙이다.
 const x = 1;
 
 function foo() {
     const x = 10;
     bar();
 }
 function bar() {
     console.log(x); // 1
 }
 foo();
 
클로저는 이 렉시컬 스코프의 규칙을 기반으로, "이미 생명 주기가 끝난 외부 함수의 변수를 참조하는
현상"이다.
 function foo() {
     const x = 1;
     return function bar() {
         console.log(x); // 1
     }
 }
 
 const savedBar = foo(); // foo는 실행 종료
 savedBar(); // 하지만 여전히 x에 접근 가능
 
즉:
- 렉시컬 스코프: 상위 스코프를 결정하는 규칙
 - 클로저: 이 규칙을 활용해 이미 종료된 함수의 변수를 참조하는 현상
 - 클로저는 렉시컬 스코프라는 규칙이 있기에 가능한 현상이라고 볼 수 있다.