클로저 정의
클로저(Closure)는 자유변수에 접근할 수 있는 내부함수 또는 그 환경을 포함하는 코드를 지칭한다.
MDN(Mozilla Developer Network) [모질라 재단및 다른 IT기업들이 사용하는 웹 개발을 위한 문서 저장소이자 수많은 프로그래밍 입문자들을 위한 학습 장소] 에서는 다음과 같이 정의한다.
“A closure is the combination of a function and the lexical environment within which that function was declared.”
클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.
응? 자유변수가 무엇인가? 렉시컬 환경은 또 무엇인가?
분명 정의는 됐는데 알아들을 수가 없다.
이 문장을 이해하기 위해 일단 자유변수부터 차근차근 알아보자.
변수의 범위(Scope)와 수명(Lifetime)
자바스크립트 변수들은 지역(local) 또는 전역(global)의 변수 범위를 갖는다
// 지역변수
function printName(){
var name = "Jae";
console.log(name);
}
// 전역변수
var name = "Jae";
function printName(){
console.log(name);
}
function printName(){
name = "Jae"; // 전역변수가 됨
console.log(name);
}
위 예시처럼
정해진 함수내에서만 사용할 수 있는 변수가 지역변수이고,
윈도우(window) 객체에 속하여 페이지 내 모든 스크립트에서 사용되어질 수 있는것이 전역변수이다.
주의: var, let, const 키워드를 사용하지않고 함수내에 선언이 되어도 자동으로 전역변수가 되어버린다. 고로 조심하자.
이것도 웬만해서 모두가 알고있겠지만,
지역변수의 수명은 짧다. 함수가 호출될 때 함수 안에서 생성되고, 함수가 종료될 때 지워진다.
전역변수는 애플리케이션(윈도우 또는 웹페이지)가 살아 있는 동안 살아있다.
중첩 함수(Nested Function) 및 함수 반환
자바스크립트는 일급 함수를 지원하므로
- 함수를 변수에 저장
- 파라미터로 함수를 넘기기
- 함수를 반환
이 가능하다!
function makePrinter() {
var outer_name = "재횬";
function printName() {
var inner_name = "다욘";
console.log(outer_name);
console.log(inner_name);
}
return printName;
}
var print = makePrinter();
클로저 - 자유변수 떡밥 회수
그럼 이제 다음 코드를 실행하면 어떻게 되는지 보자
function makePrinter() {
var outer_name = "재횬";
function printName() {
var inner_name = "다욘";
console.log(outer_name);
console.log(inner_name);
}
return printName;
}
var print = makePrinter();
print();
//출력
재횬
다욘
분명 outer_name 변수와 inner_name 변수는 함수내부에서 선언된 지역변수이다.
지역변수의 수명은? 답: 함수가 종료되는 시점에서 사라진다.
하지만 makePrinter() 호출 후에 print() 호출에도 살아서 출력을 해준다는 것!
클로저는 자신을 포함하고 있는 외부 함수의 인자, 지역변수 등을 외부 함수가 종료된 후에도 사용할 수 있다.
이러한 변수를 자유변수(free variable)라고 부른다.
그리고 이러한 자유변수를 가지는 코드를 클로저라고 한다.
렉시컬 스코핑(lexical scoping)
클로저는 어휘적 범위 지정(lexical scoping)의 한 예이다.
"lexical"이란, 어휘적 범위 지정(lexical scoping) 과정에서 변수가 어디에서 사용 가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지 고려한다는 것을 의미한다.
스코프는 함수를 호출할 때가 아니라 함수를 어디에 선언하였는지에 따라 결정된다. 이를 렉시컬 스코핑(Lexical scoping)라 한다.
클로저를 어디에 쓸까?
처음 이 개념을 접하면서 가장 먼저든 생각은 '도대체 이걸 어디다가 쓰지?' 였다.
다시하면 클로저의 가장 중요한 포인트를 생각해보면 자유변수를 갖는 것이다.
자유변수는 내부변수를 외부로 끌어내는 것 같으면서도 내부변수에 직접적으로 접근할 수가 없다.
// 카운터를 위한 클로저를 생성
var counter = (function() {
// private 변수 입니다.(외부 접근 불가)
var privateCounter = 0;
// private 함수 입니다.(외부 접근 불가)
function changeCounter(val) {
privateCounter += val;
}
// public 함수를 가지는 객체를 반환
return {
// 증가 기능을 가지는 public 함수
inc: function() {
changeCounter(1);
},
// 감소 기능을 가지는 public 함수
dec: function() {
changeCounter(-1);
},
// public 함수 입니다.(현재값 조회)
val: function() {
return privateCounter;
}
};
})();
counter.inc();
counter.inc();
console.log("after increment : " + counter.val());
counter.dec();
console.log("after decrement : " + counter.val());
결과)
after increment : 2
after decrement : 1
var counter = (function() {})();
위와같은 형식으로 자기호출 함수로 하나만 생성을 했다.
1. 카운터 값을 유지하는 변수는 자유 변수로 카운터 밖에서는 직접 접근할 수 없다.
2. 자기 호출 함수는 호출시 최초 한번만 실행되므로 var counter가 생겨남과 동시에 0으로 초기화된다.
3. 클로저 내부에 정의된 카운터 값을 변경하는 changeCounter() 함수는 외부에서는 접근할 수 없는 private 함수가 된다.
이처럼 클로저를 통해 객체지향언어의 객체가 가지는 private 멤버 변수, private 메소드, public 메소드와 유사한 기능을 구현할 수 있다.
출처:
https://offbyone.tistory.com/135 [쉬고 싶은 개발자]
https://hyunseob.github.io/2016/08/30/javascript-closure/ [DailyEngineering]
'JavaScript' 카테고리의 다른 글
[JavaScript] 이벤트 버블링(Event Bubbling), 이벤트 캡처링(Event Capturing) (2) | 2022.10.04 |
---|---|
[JavaScript] 이벤트(Event)란? (1) | 2022.10.03 |
[JavaScript] 자바스크립트(JavaScript)의 역사 (1) | 2022.10.03 |
[Javascript] 호이스팅(Hoisting)이란? (1) | 2021.10.14 |
[Javascript] var, let, const 차이 (2) | 2021.10.13 |