자바스크립트에서는 실행 중인 함수, 코드 블록 {...}, 스크립트 전체는 렉시컬 환경(Lexical Environment)라는 내부 숨김 연관 객체(internal hidden associated object) 를 갖는다.
자바스크립트 엔진이 함수를 생성하면, 그 함수는 '함수 객체'와 '함수가 어디서 선언되었는지' 에 대한 정보(렉시컬 환경의 스냅샷)를 갖게 된다.
함수는 [[Environment]] 같은 내부 슬롯을 통해 선언될 때 렉시컬 환경(이론 객체)을 참조하게 된다.
변수는 내부 슬롯에서 참조하지 않고 렉시컬 환경 내부의 환경 레코드에 등록이 되어있다. 즉, 변수는 환경 레코드에 프로퍼티 형태로 기록되어 있을 뿐이며, 검색해서 value를 찾는 구조이다.
그래서 무엇이 특별한가?
이로 인해 특별해 보이는 것은 두 가지이다.
함수가 선언되기 전에 사용이 가능한 것 (호이스팅)
test();
function test(){
return console.log('함수 선언 전 호출');
}
C#같은 정적 언어는 컴파일 타임에 코드를 분석해서 파악하므로 런타임 시점에는 당연히 호출이 가능하다. 반면 자바스크립트는 런타임 시 스코프(실행 컨텍스트) 생성 단계에서 함수 선언문을 먼저 등록하므로 호이스팅 현상이 일어난다.
조금 요약하여 절차를 설명하자면
- C#은 컴파일에서 분석완료 -> 런타임
- 자바스크립트는 런타임 -> 실행 컨텍스트(함수 수집) [호이스팅] -> 코드 순차 실행
그렇기에 내부 동작 구조는 다르지만 비슷한 동작이 되는 것처럼 느끼는 것이다.
그러나 함수 표현식 에서는 호이스팅이 적용되지 않는다.
// 함수 표현식(Expression)
test(); // Error: Cannot access 'test' before initialization
const test = function() {
console.log('함수 표현식 에러 예시');
}
함수 표현식은 실행 컨텍스트에서 함수 수집의 대상이 아니므로 위와 같이 적용이 되지 않는다.
함수마다 독립적인 렉시컬 환경을 갖는다.
function makeCounter() {
let count = 0;
return function() {
return count++;
};
}
let counter = makeCounter();
let counter2 = makeCounter();
alert( counter() ); // 0
alert( counter() ); // 1
alert( counter2() ); // 0
alert( counter2() ); // 1
makeCounter()로 각각의 변수를 선언하였기 때문에 독립적인 렉시컬 환경을 갖는다.
클로저(closure)
함수가 선언될 때 렉시컬 환경을 기억하여, 스코프 밖에서 호출하더라도 스코프의 변수에 접근할 수 있도록 하는 메커니즘이다.
위에서 얘기했듯이 함수가 선언될 때 [[Environment]]를 기억하기 때문에, 함수가 호출되어 끝났음에도 메모리에 남아 계속해서 살아남는(?) 메커니즘을 의미한다.
<script>
function getNumber(){
let number = 5;
function innerGetNumber(){
return number;
}
return innerGetNumber;
}
let runner = getNumber();
console.log(runner());
</script>
위와 같은 HTML 코드를 실행하여 브라우저 환경에서 return number 를 디버깅하면 아래와 같이 클로저를 볼 수 있다.
그렇다면 이렇게 복잡해보이는 클로저를 왜 쓰는 것일까? 이유는 아래와 같다.
- 정보 은닉 (Information Hiding)
- 외부에선 직접 수정 불가능하고, 내부 함수(클로저)를 통해서만 접근할 수 있는 형태를 만들 수 있습니다.
- 이는 객체지향 언어에서의 ‘private 멤버’ 같은 역할을 간단히 흉내낼 수 있는 방식이 됩니다.
- 상태 유지 (Stateful Function)
- 호출이 반복되어도 변수를 계속 기억하므로, 함수가 어떤 상태를 “캡처(capture)”하여 유지할 수 있습니다.
- 모듈 패턴 (Module Pattern)
- 여러 함수를 하나의 외부 함수 안에 두고, 필요한 멤버만 외부로 노출(리턴)하는 방식으로 모듈화된 코드를 작성할 수 있습니다.
'프로그래밍 > 자바스크립트' 카테고리의 다른 글
이벤트 루프 (Event Loop) (0) | 2025.01.08 |
---|---|
함수 (function) (0) | 2025.01.06 |
변수 선언과 변수 타입 (0) | 2025.01.06 |