오늘은 자바스크립트 '클로저 (Closure)' 개념에 대해 조사하고 정리하는 시간을 가져보자.
먼저, 클로저의 개념을 알기전에 렉시컬 스코프 관한 이해가 필요하다.
'자바스크립트는 언어 특성상 함수가 정의되는 시점에 상위 스코프가 결정된다.'
이 말의 의미를 예시를 통해 살펴보자
function init() {
var name = "Mozilla";
function displayName() {
alert (name);
}
displayName();
}
init();
init이라는 외부함수가 존재함고 그 내부에 displayName의 이름을 가지는 내부함수를 '정의'하고 있다.
displayName 함수가 init이라는 함수 내부에서 정의되고 있으므로, init 함수를 상위스코프로 가진다.
이 부분에서 중요한 점은 실행되는 시점이 중요한게 아니라 함수가 정의되는 시점을 봐야하는 것이다.
조사하면서 이해에 도움이 되는 설명이 있었는데 '자식 함수는 부모 함수를 어휘적으로 묶는 함수라고 부릅니다.'
이 설명대로 displayName 자식함수가 init이라는 부모함수를 묶었고, 부모함수에 정의된 name이라는 변수에 접근할 수 있었던 것이다!
이 렉시컬 스코프가 컴파일 시점에 결정되므로 정적스코프라고 불린다.
위 사진에서 보는 것과 같이 최하단의 내부함수는 상위스코프 나아가 전역 스코프 변수들에 접근할 수 있지만, 반대의 경우 외부에서는 내부의 변수들로 접근할 수 없다.
클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다 - MDN
MDN에서 위 설명을 들었을 때 도저히 클로저가 뜻하는게 먼지 이해가 되지 않았다.
그럼 위 말을 이해하기 위해 예시를 통해 살펴보자
function makeFunc() {
var name = "Mozilla";
function displayName() {
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();
makeFunc라는 외부함수를 만들고 그 내부에 displayName 내부함수를 선언하였다.
makeFunc()를 실행하면서 내부함수를 리턴해주면서 외부함수는 실행이 완료되었다. (함수의 생명주기가 끝났다.)
그렇다면 외부함수에 정의된 'name' 변수 생명이 다하여 사용할 수 없을 것이라고 생각하지만 우리가 배우고 있는 이 '클로저 (myFunc)' 아이 덕분에 외부함수를
통해 리턴받은 내부함수 (displayName)을 실행했을 때 'Mozilla'가 출력되는 것을 확인할 수 있다.
클로저를 통해 렉시컬 스코프를 통해 정의된 상위 스코프를 기억 (참조)하고 있다!
비유를 하자면 클로저를 통해 패키지를 형성(1 + 1, 동봉) 했다! 라고 볼 수 있을 거 같다.
클로저를 통해 변수를 은닉화 (hiding) 시킬 수 있다.
위 말을 이해하기 위해 아래 예시를 살펴보자
function makeCounter() {
let num = 0;
return function () {
return num++;
};
}
let counter = makeCounter();
console.log(counter());
console.log(counter());
console.log(counter());
0
1
2
makeCounter 외부함수에서 num을 선언하고 0으로 초기화한다. 그리고 num을 리턴 후 값을 1씩 증가씨키는 내부함수를 리턴한다.
우리가 배운 클로저 개념을 도입해보면 외부함수가 리턴하는 익명함수가 클로저가되서 상위 스코프에 정의된 'num' 변수와 함께 패키지로 'counter' 변수에 담길 것이다.
counter 함수를 호출하면서 num 변수가 증가하는 것을 볼 수 있다. 이제 num 변수는 오직 counter (클로저) 함수를 통해서만 값을 조작할 수 있다. 우리가 임의로 num 변수에 값을 할당하거나, 변경할 수 없게되었다.
이렇게 클로저를 통해 은닉화에 이점을 가져갈 수 있다!
MDN
Lexical scope in JavaScript
자바스크립트 중급 강좌 #11 클로저(Closure) 5분만에 이해하기
[10분 테코톡] 🍧 엘라의 Scope & Closure
피드백은 언제든 환영합니다 🙏