자바스크립트 클로저(closure)는 함수와 함수가 선언됐을 때의 렉시컬 환경(lexical environment)의 조합입니다. 이를 이해하기 위해서는, 자바스크립트에서의 변수 스코프(scope)와 렉시컬 환경에 대한 이해가 필요합니다.
스코프는 변수가 유효한 범위를 나타내며, 자바스크립트에서는 전역 스코프(global scope)와 지역 스코프(local scope)로 나눠집니다. 전역 스코프는 코드 전체에서 접근 가능한 스코프이며, 지역 스코프는 함수 내부에서 선언한 변수가 접근 가능한 스코프입니다.
렉시컬 환경은 변수와 값의 매핑을 저장하는 객체입니다. 함수가 호출될 때마다 새로운 렉시컬 환경이 생성되며, 그 함수 내에서 선언한 변수와 값이 저장됩니다. 또한, 함수가 선언될 때의 렉시컬 환경을 기억하여 이후에도 사용할 수 있습니다.
1. 기본 예제
function outerFunction() {
var outerVariable = "I am in the outer function";
function innerFunction() {
console.log(outerVariable);
}
return innerFunction;
}
var innerFunc = outerFunction();
innerFunc(); // 출력값: "I am in the outer function"
위 예제에서, outerFunction은 innerFunction을 반환합니다. innerFunction은 outerFunction의 지역 변수 outerVariable에 접근할 수 있습니다. 그리고 outerFunction이 반환된 이후에도 innerFunction은 outerVariable에 접근할 수 있습니다. 이는 클로저의 개념입니다.
2. 함수 팩토리 예제
클로저는 함수가 반환된 이후에도, 함수가 선언됐을 때의 렉시컬 환경을 기억하여 해당 함수가 호출될 때 사용할 수 있게 합니다. 이를 이용하여, 함수를 반환하는 함수(함수 팩토리)를 만들 수 있습니다.
function counter() {
var count = 0;
function innerFunc() {
count++;
console.log(count);
}
return innerFunc;
}
var increment = counter();
increment(); // 출력값: 1
increment(); // 출력값: 2
increment(); // 출력값: 3
위 예제에서, counter 함수는 count 변수와 innerFunc 함수를 반환합니다. innerFunc 함수는 count 변수를 클로저로서 기억하고 있으며, 호출될 때마다 count 변수를 증가시키고 그 값을 출력합니다.
3. 콜백 함수 예제
클로저는 이외에도 다양한 용도로 사용될 수 있습니다. 예를 들어, 콜백 함수(callback function)를 사용하는 비동기 코드에서 클로저를 활용할 수 있습니다.
function asyncFunction(callback) {
setTimeout(function() {
var result = "Async function completed";
callback(result);
}, 1000);
}
asyncFunction(function(result) {
console.log(result);
});
위 예제에서, asyncFunction은 1초 후에 실행되는 콜백 함수를 호출합니다. 이때 클로저를 사용하여 result 변수를 기억하고, 콜백 함수에서 result 변수를 사용할 수 있게 합니다.
4. private 변수와 메서드 구현 예제
클로저는 또한 private 변수와 메서드를 구현하는 데에도 사용될 수 있습니다. 객체 리터럴을 사용하여 private 변수와 메서드를 구현하는 방법은 다음과 같습니다.
var counter = (function() {
var count = 0;
function increment() {
count++;
console.log(count);
}
function decrement() {
count--;
console.log(count);
}
return {
increment: increment,
decrement: decrement
};
})();
counter.increment(); // 출력값: 1
counter.increment(); // 출력값: 2
counter.decrement(); // 출력값: 1
위 예제에서, 클로저를 사용하여 count 변수를 private으로 만들고, increment와 decrement 메서드를 public으로 만들어 객체 리터럴 형태로 반환합니다.
5. 함수 인자 저장 예제
또한, 클로저는 함수의 인자를 저장하는 것과 같은 특정한 상황에서도 유용합니다. 예를 들어, 다음과 같은 코드를 살펴보세요.
function sayHello(name) {
return function() {
console.log("Hello, " + name);
};
}
var helloJohn = sayHello("John");
var helloJane = sayHello("Jane");
helloJohn(); // 출력값: "Hello, John"
helloJane(); // 출력값: "Hello, Jane"
위 예제에서, sayHello 함수는 인자로 받은 name 변수를 클로저로서 기억합니다. 이후에 sayHello 함수에서 반환된 함수가 호출될 때마다, 클로저에 저장된 name 변수를 사용하여 "Hello, [name]" 문자열을 출력합니다.
이러한 클로저의 특징으로 인해, 메모리 누수(memory leak)가 발생할 수 있습니다. 클로저를 사용하는 코드에서는 불필요한 변수나 함수를 클로저로서 기억하게 되면 해당 변수나 함수가 더 이상 필요하지 않아도 메모리에 남아있을 수 있습니다. 따라서, 클로저를 사용할 때에는 주의하여 사용해야 합니다.
6. 성능 이슈
클로저를 사용하는 것이 성능상 느릴 수 있는 경우가 있습니다. 이는 클로저를 사용하면 내부 함수가 외부 변수에 접근할 때마다 스코프 체인(scope chain)을 탐색해야 하기 때문입니다.
스코프 체인은 내부 함수에서 참조하는 변수를 찾기 위해 외부 함수부터 전역 객체까지 모든 스코프를 순회하면서 변수를 찾습니다. 이 과정에서 많은 시간이 소요될 수 있으며, 이는 성능 저하를 일으킬 수 있습니다.
또한, 클로저를 사용하면 메모리 누수(memory leak)가 발생할 수 있습니다. 클로저가 참조하는 변수나 함수가 더 이상 필요하지 않더라도 클로저가 살아있는 한 해당 변수나 함수는 메모리에 계속 남아있을 수 있습니다.
따라서, 클로저를 사용할 때에는 성능상 고려할 점이 있습니다. 불필요한 스코프 체인 탐색을 피하기 위해, 내부 함수에서 참조하는 변수는 외부 함수의 매개변수나 지역 변수로 전달하여 사용하는 것이 좋습니다. 또한, 클로저가 참조하는 변수나 함수가 더 이상 필요하지 않으면 해당 변수나 함수를 참조하는 클로저도 같이 제거하여 메모리 누수를 방지해야 합니다. 이를 위해, 자바스크립트 엔진이 가비지 컬렉션을 수행할 때 해당 클로저가 더 이상 필요하지 않다는 것을 인지할 수 있도록 변수나 함수를 참조하는 클로저의 수를 최소화해야 합니다.
'자바스크립트' 카테고리의 다른 글
[자바스크립트] 프라미스 (promise) (0) | 2023.03.09 |
---|---|
[자바스크립트] 콜백 함수 (callback function) (0) | 2023.03.09 |
[자바스크립트] 렉시컬 환경 (Lexical Environment) (0) | 2023.03.09 |
[자바스크립트] 콜스택 (Call stack) (0) | 2023.03.09 |
[자바스크립트] 호이스팅 (Hoisting) (0) | 2023.03.08 |
댓글