먼저, 클로저에 대해 살펴보기 전에 스코프에 대한 개념을 간단히 정리해보자
스코프(scope)
스코프는 변수가 접근할 수 있는 범위를 뜻한다. 자바스크립트에서는 전역 스코프와 지역 스코프가 있다.
전역 스코프
전역 스코프에 선언된 변수는 코드 어디서든 접근 가능하다. 전역 스코프에 변수를 선언하려면 함수나 블록 외부에서 선언하면 된다.
var globalVar = 'I am global'; // 전역 변수
function globalExample() {
console.log(globalVar); // 'I am global'
}
globalExample();
console.log(globalVar); // 'I am global'
지역 스코프
js에서 지역 스코프는 블록 스코프와 함수 스코프로 나뉘어진다.
보통의 경우 함수 스코프는 없지만(js에만 있는 듯?하다 거의) js에서 var를 사용하면 이는 함수 스코프를 가진다. ES6에서 let과 const 키워드로 도입하여 사용할 수 있게 되었고, 중괄호 {}로 둘러싸인 코드 블록 내에서 유효하다.
//함수 스코프
function localScopeExample() {
var localVar = 'I am local';
console.log(localVar); // 'I am local'
}
localScopeExample();
console.log(localVar); // ReferenceError: localVar is not defined
//블록 스코프
{
let blockScopedVar = 'I am block scoped';
console.log(blockScopedVar); // 'I am block scoped'
}
console.log(blockScopedVar); // ReferenceError: blockScopedVar is not defined
아래 예제를 보면 함수가 호출되는 위치가 아니라 함수가 선언되는 위치에 기반하여 상위 스코프가 결정되는 것을 알수 있다. 이를 렉시컬 스코프(정적 스코프) 라고 한다.
var x = 1;
function func1() {
var x = 10;
bar();
}
function func2() {
console.log(x);
}
func1(); // 1
func2(); // 1
렉시컬 스코프는 함수를 어디에 선언하였는지에 따라 상위 스코프가 결정되는 것을 말한다. 함수가 호출되는 위치가 아니라, 함수가 선언된 위치를 기준으로 스코프 체인이 결정되는 것
대부분의 프로그래밍 언어는 렉시컬 스코프를 따르지만, Bash 같은 쉘 스크립트 언어는 함수가 호출되는 위치를 따르는 동적스코프를 따른다고 한다. 정리하자면,
- 렉시컬 스코프(정적 스코프): 함수가 선언된 위치를 기준으로 스코프 체인이 결정
- 동적 스코프: 함수가 호출되는 위치에 따라 상위 스코프가 결정
클로저
클로저는 함수와 그 함수가 선언된 렉시컬 환경의 조합이다. 함수가 자신이 선언된 스코프를 기억하고, 그 스코프에 접근할 수 있는 기능을 제공한다.
쉽게 말하여 클로저는 다음과 같다.
- 자신이 선언된 당시의 환경(==렉시컬 스코프)을 기억하는 것
- 자신이 생성될 때의 스코프에 있는 변수들을 참조할 수 있는 것
클로져 예제
function outerFunc() {
let outerVar = 'I am outside!';
function innerFunc() {
console.log(outerVar); // 'I am outside!'
}
return innerFunc;
}
const myClosure = outerFunc();
myClosure(); // 'I am outside!'
1. outerFunc
- outerFunc은 outerVar 변수를 선언하고, innerFunc을 정의
- innerFunc은 outerVar를 참조
- outerFunc은 innerFunc을 반환
2. 클로저 형성
- const myClosure = outerFunc(); 호출 시, outerFunc은 실행되어 innerFunc을 반환
- 이 반환된 innerFunc은 outerFunc의 스코프를 기억
- myClosure는 이제 innerFunc을 참조하며, outerVar에 접근
3. 클로저의 활용
- 클로저를 통해 함수가 생성된 환경을 기억하고, 나중에 호출될 때 그 환경에 있는 변수들을 참조
사실, 프로그래밍을 조금 하다보면 명확히 아는 개념은 아니지만 자연스럽게 생각하던 사고 방식이다.
클로저를 이용해 정보은닉과 캡슐화도 가능하다
클로저 정보은닉 예제
function createCounter() {
let count = 0; // 비공개 변수
return {
increment: function() {
count++;
return count;
},
decrement: function() {
count--;
return count;
},
getCount: function() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
console.log(counter.getCount()); // 1
console.log(counter.count); // undefined (비공개 변수에 접근 불가)
간단한 카운터 예제이다.
- count 변수를 선언하고, 이는 함수 외부에서 직접 접근할 수 없다.
- 그에 반해 다른 메서드들은 클로저를 통해 count 변수에 접근하여 값을 변경하거나 반환 가능하다.
count 변수가 비공개 변수라고 가정했을 때, 정보은닉과 캡슐화 무결성을 지킬 수 있다.
'Frontend > JavaScript' 카테고리의 다른 글
[js] this (feat. arrow function) (1) | 2024.06.16 |
---|---|
[js] Template Literals, Tagged Templates (0) | 2024.06.13 |
[js] 호이스팅과 TDZ (0) | 2024.05.26 |
[js] export default const가 안된다 : import, export (0) | 2024.05.07 |
[js] Ajax : fetch와 axios를 더한 (0) | 2024.04.29 |