자바스크립트 복습을 하던 중 클로저가 아직 헷갈려 다시 공부하며 정리해 보고자 한다.
클로저를 공부하기 전 스코프에 대한 개념 정리 및 이해가 필요하다.
클로저(Closure)란?
JS에서 중요한 개념이지만, JS 고유의 개념이 아닌 함수형 프로그래밍 언어에서도 사용되는 특성이다.
MDN에서 정의하는 클로저
클로저란 함수와 함수가 선언된 어휘적 환경 (lexical environment)의 조합이다.
즉, 클로저는 내부 함수에서 외부 함수의 범위에 대한 접근을 제공. JavaScript에서 클로저는 함수가 생성될 때마다 생성
🌿 클로저의 기본 개념
- 함수와 그 함수가 선언된 렉시컬 환경의 조합으로, 내부 함수가 외부 함수의 변수에 접근할 수 있게 해 준다.
- 함수가 자신의 외부 스코프에 있는 변수에 접근할 수 있는 기능을 제공
스코프(Scope)
- 변수와 함수의 유효 범위
- 함수 스코프(function scope)와 블록 스코프(block scope, let이나 const를 사용할 때)
const x = 1;
function outerFunc() {
const x = 10;
function innerFunc() {
console.log(x); // 10
}
innerFunc();
}
outerFunc();
outerFunc 함수 내부에 중첩 함수 innerFunc가 정의된다. 이때 중첩 함수 innerFunc의 상위 스코프는 외부 함수 outerFunc의 스코프다.
따라서 중첩 함수 innerFunc 내부에서 자신을 포함하고 있는 외부 함수 outerFunc의 변수 x에 접근할 수 있다.
이는 자바스크립트가 렉시컬 스코프를 따르는 프로그래밍 언어이기 때문이다.
- 위 예제에서 클로저는 outerFunc를 호출해 반환된 innerFunc이다. 이 함수는 x에 접근할 수 있는 클로저다.
🌿 클로저의 특징
- 외부함수 실행 종료 후에도 외부함수의 스코프(함수가 선언된 어휘적 환경)에 접근 가능
- 외부 함수 스코프에서 내부 함수 스코프에 직접 접근할 수는 없지만, 내부 함수는 외부 함수의 스코프에 접근할 수 있다.
- 함수가 선언되어 있던 환경(어휘적 환경)을 기준으로 변수 조회
- 클로저를 각각의 변수에 할당할 경우, 각 클로저는 독립적인 상태를 보존할 수 있다.
🌿 클로저의 장점
- 특정 변수를 외부에서 접근하지 못하도록 정보 은닉 가능(캡슐화). 의도하지 않은 수정 방지 가능
- 여러 상태를 관리할 수 있는 기능 제공
- 전역변수 사용을 최소화 함으로써 예외적인 상황에 대한 실수 방지 가능
- 모듈화에 유리하여 코드 재사용성을 높이고, 이해도를 증가시킨다.
🌿 클로저의 단점
- 외부 변수를 계속 참조하기 때문에 메모리 사용량이 증가할 수 있으며, 사용하지 않는 클로저를 잘 관리하지 않으면 메모리 누수가 발생할 수 있다. 특히 이벤트 리스너나 타이머와 함께 사용할 때는 클로저가 참조하는 변수들이 계속 메모리에 남아 성능에 영향을 줄 수 있다. 따라서 사용하지 않는 클로저를 적절히 정리하여 메모리 누수를 방지해야 한다.
- 클로저 내부에서 변수가 어떻게 사용되는지 추적하기 어려워 디버깅이 어렵다. 여러 중첩 함수와 클로저가 있을 경우, 특정 변수의 상태나 변경 사항을 파악하기 힘들어질 수 있다.
✦ 어휘적 범위 지정(렉시컬 스코핑; Lexical scoping)
렉시컬 스코핑은 함수를 호출할 때가 아니라 어디에 선언하였는지에 따라 상위 스코프가 결정된다.
즉, 함수가 선언된 위치를 기준으로 상위 스코프를 결정한다.
렉시컬 스코핑은 자바스크립트뿐만 아니라 많은 프로그래밍 언어에서 사용하는 스코프 규칙이다. 이 규칙은 스코프 체인이 함수가 선언된 위치에 의해 결정되므로, 어떤 변수를 찾을 때 가장 가까운 스코프부터 검색한다.
function outer() {
let x = 'outer x';
function inner() {
console.log(x); // 'outer x'
}
inner();
}
outer();
✦ 어휘적 환경(Lexical environment)
JS에서 변수를 저장하고 접근할 수 있는 환경을 의미한다. 변수는 특정 스코프 내에서 선언되며, 이 스코프는 해당 변수가 유효한 범위를 결정한다.
Lexical Environment는 두 가지 중요한 구성 요소를 포함한다.
- 환경 레코드 (Environment Record): 해당 스코프 내에서 선언된 변수와 함수에 대한 정보를 저장
- 외부 렉시컬 환경의 참조 (Reference to Outer Lexical Environment): 외부 환경을 참조할 수 있게 하여, 상위 스코프에서 변수를 찾을 수 있도록 한다. 이는 클로저를 가능하게 하며, 내부 함수가 외부 함수의 변수를 접근할 수 있게 한다.
함수가 생성될 때마다 자체 Lexical Environment가 생성된다. 만약 해당 함수가 다른 함수를 호출하면, 호출된 함수는 자신의 Lexical Environment를 생성하고 외부 Lexical Environment를 참조할 수 있다.
function outer() {
let x = 'outer x'; // outer 함수의 환경 레코드에 저장
function inner() {
let y = 'inner y'; // inner 함수의 환경 레코드에 저장
console.log(x); // 'outer x' - 외부 환경을 참조
}
inner(); // inner 함수 호출
}
outer(); // 'outer x' 출력
실행 컨텍스트(Execution Context)와의 관계
JS에서 코드가 실행될 때마다 실행 컨텍스트가 생성된다. 실행 컨텍스트는 코드의 실행에 필요한 정보를 포함하며, 렉시컬 환경은 실행 컨텍스트의 중요한 부분이다.
실행 컨텍스트는 다음과 같은 구성 요소를 포함한다.
- Lexical Environment : 현재 실행 컨텍스트에서 접근 가능한 변수와 함수의 정보 저장
- Variable Object : 실행 컨텍스트가 생성될 때의 변수 및 함수 선언을 포함
- this 바인딩 : 현재 실행 컨텍스트의 this 값을 참조
각 함수가 호출될 때 새로운 실행 컨텍스트가 생성되며, 해당 컨텍스트의 렉시컬 환경은 함수의 환경 레코드와 외부 렉시컬 환경의 참조를 포함한다. 이를 통해 함수는 자신의 스코프뿐만 아니라 상위 스코프의 변수에도 접근할 수 있다.
✦ 스코프 체인(Scope Chain)
클로저가 변수에 접근할 때, 스코프 체인을 통해 변수 검색이 이루어진다. 클로저는 자신이 속한 스코프와 그 외부의 스코프를 모두 포함한다.
✦ 변수 유효성(Variable Accessibility)
클로저는 변수가 선언된 시점에 스코프를 기억하기 때문에, 외부 함수가 종료된 이후에도 변수를 사용할 수 있게 해 준다.
Refer )
- ChatGPT
- MDN
- 모던 자바스크립트 Deep Dive
'기술면접' 카테고리의 다른 글
프론트엔드 아키텍처 패턴 (5) | 2024.11.04 |
---|---|
스코프(Scope) (0) | 2024.10.07 |
호이스팅(Hoisting)과 TDZ (0) | 2024.10.05 |