JavaScript의 변수를 정리하면서 클로저에 대한 필요성을 볼 수 있었습니다.
[참조 : https://roothyo.tistory.com/83?category=1108573] * JavaScript - 변수(variable)
Mdn에서 정의한 클로저는 다음과 같습니다.
클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다.
클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야 한다.
JavaScript에서 함수는 일급 객체로 다른 함수의 인자로 전달이 가능합니다. 또 함수가 다른 함수를 리턴하는 경우가 가능합니다. (* funargs : functional arguments)
첫째는 upward funargs problem입니다. 이 문제는 함수가 다른 함수를 리턴하고 이미 선언된 변수를 사용하면서 발생합니다. 이를 해결하기 위해서 각 함수가 생성이 되는 순간에 Scope 프로퍼티를 사용해서 부모의 정보를 저장해두고 새로운 스코프 체인 정보를 생성하게 됩니다.Scope Chain = Activation object + [[scope]]
(function init() {
var name = "Mozilla"; // 지역 변수
function displayName() { // displayName() 클로저
console.log(name); // displayName() 부모함수의 정의된 name 사용
}
displayName();
})()
displayName() 함수내의 console.log에서 name의 값을 성공적으로 출력합니다.이 예시를 통해 함수가 중첩된 상황에서 파서가 어떻게 변수를 처리하는지 알 수 있으며 이는 lexical scoping의 한 예 입니다.
"lexical"이란, 어휘적 범위 지정(lexical scoping) 과정에서 변수가 어디에서 사용 가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지 고려한다는 것을 의미한다.
다음 예제를 보면 이전 컨텍스트의 실행이 끝난 후여도 displayName에서 차몾하고 있던 name의 값은 그대로 유지함을 볼 수 있습니다. 이를 통해 부모의 정보를 저장해두고 새로운 스코프 체인 정보를 생성하게 됩니다.
function makeFunc() {
var name = "roothyo";
function displayName(){
console.log(name);
}
return displayName
}
var myFunc = makeFunc();
myFunc();
두번째 발생하는 문제는 Downward funargs problem 입니다.
이 경우에는 부모 컨텍스트가 존재하지만 내부 함수가 어떤 변수를 사용해야 할지 애매모호한 경우가 있습니다. 정적으로 함수가 생성이 될 때, 만들어진 컨텍스트의 정보를 사용할 것인지 아니면 실행시점에 동적으로 생성된 컨텍스트의 변수 정보를 사용할 것인지 하나를 선택해야 하는 상황이 발생합니다.
var x = 10;
function foo(){
console.log(x)
}
(function (funArg) {
var x = 20;
funArg();
})(foo);
이 경우 funArg() 함수는 어떤 x를 사용해야 하는지 애매모호 할 수 있는데, 함수가 실행되는 시점이 아닌 생성이 되는 시점에 참조하는 x 값을 그대로 사용하게 됩니다. 따라서 10을 출력합니다.
함수를 반복문을 통해 생성하는 경우에도 혼란을 줄 수 있습니다.
var data = [];
for (var k = 0; k < 3 ; k++) {
data[k] = function(){
console.log(k);
}
}
data[0]();
data[1]();
data[2]();
이 세번의 호출은 0, 1, 2를 호출 할 것 같지만 답은 3, 3, 3입니다.
반복문을 돌면서 각기 다른 스코프가 생성이 되어야 하는데 이경우에는 전역 스코프 상의 k를 공유하면서 k값만 계속 변화가 일어나게 됩니다. 즉 공통으로 k를 바라보기 때문입니다.
따라서, 0, 1, 2를 리턴하기 위해서는 다음과 같은 코드로 변환해야 합니다.
var data = [];
for (var k = 0; k < 3 ; k++) {
data[k] = (function(){
return function(){
console.log(k);
}
})
}
data[0]();
data[1]();
data[2]();
실용적인 클로저
클로저는 어떤 데이터(어휘적 환경)와 그 데이터를 조작하는 함수를 연관시켜주기 때문에 유용합니다.
객체가 어떤 데이터와 하나 혹은 그 이상의 메소드들을 연관시킨다는 점에서 객체지향 프로그래밍과 비슷합니다.
결론적으로 오직 하나의 메소드를 가지고 있는 객체를 일반적으로 사용하는 모든 곳에 클로저를 사용할 수 있습니다.
프론트 엔드 자바스크립트에서 우리가 쓰는 많은 코드가 이벤트 기반인데, 우리는 몇 가지 동작을 정의한 다음 사용자에 의한 이벤트에 연결하고 일반적으로 콜백으로 첨부되는 단일 함수입니다.
마치 Factory pattern과 비슷한 느낌일 때 주로 사용됩니다.
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
Python에서도 똑같이 closure가 동작하는 것을 알고는 있었지만, 자세하게 어떤 이유로 발생이 되는지에 대해서는 처음 공부해 본 것 같아서 상당히 인상 깊었습니다. 다음 포스팅은 변수 = 메모리인 만큼 메모리의 누수를 관리하는 garbage collector에 대해 알아보겠습니다.
[출처 : https://yubylab.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%B3%80%EC%88%98%EB%A1%9C-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0] - [자바스크립트] 변수로 자바스크립트 이해하기
[출처 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures] - mdn closure
'Programming Language > JavaScript' 카테고리의 다른 글
JavaScript - Promise (프로미스) (0) | 2022.07.11 |
---|---|
JavaScript - 가비지 컬렉션(Garbage Collection) (0) | 2022.07.08 |
JavaScript - 변수(Variable) (0) | 2022.07.07 |
JavaScript - 콜백 함수(Callback) (0) | 2022.07.06 |
JavaScript - 객체(Object) (0) | 2022.07.05 |