A closure is the combination of a function and the lexical environment within which that function was declared.
클로저는 함수와 함수가 선언되던 당시의 정보를 담은 환경(어휘적 환경)의 조합이다.
MDN - closure 발췌lexical environment ? 선언 당시의 환경에 대한 정보를 담는 객체. 구성 환경.
클로저는 실행되는 장소나 시점은 관계가 없이 함수가 선언되는 주변 환경과 관련하여 생성되는 개념이다.
클로저가 형성되어 기억하게 되는 환경 정보는 함수가 선언될 당시에만 순간적으로 포착하고 기억되는 것이 아니라 지속적으로 그 변화를 추적하게 된다.
클로저는 어떤 데이터(어휘적 환경)과 그 데이터를 조작하는 함수를 연관시켜주기 때문에 유용하다.
클로저는 함수 선언시 생성되는 스코프(유효범위)와 밀접한 관계가 있다. 클로저를 이해하기 전에 어떻게 변수의 스코프를 지정하는지(Lexical scoping)를 먼저 이해해야 한다.
내부함수가 외부함수의 실행 컨텍스트에 접근 가능하다.
외부함수에서 선언한 변수(지역변수)를 내부함수가 접근할 수 있고, 내부함수를 외부함수의 외부로 전달할 경우, 외부함수가 종료된 이후에도 변수가 사라지지 않는 현상이다. 즉, 클로저는 지역변수가 함수 종료 후에도 사라지지 않게 할 수 있고(사용자가 선택 가능) 함수 내부에서 생성한 데이터와 그 유효범위로 인해 발생하는 특수한 현상/상태라고 할 수 있다.
function outer() {
var outerTit = '외부 함수';
function inner() {
var innerTit = '내부 함수';
console.log(outerTit + ' ' + innerTit);
}
inner();
}
outer();
console.log(outerTit);
outerTit
는 outer
함수 외부에서 접근 할 수 없지만, inner
함수 내부에서는 접근할 수 있다.
👉 내부함수(inner
)에서 외부함수(outer()
)의 지역변수(outerTit
)에 접근할 수 있다.
외부함수(outer()
)의 외부에서는 지역변수(outerTit
)에 접근할 수 없다.
객체지향프로그래밍은 외부와의 데이터 연동이 매우 활발히 이루어져야 한다. 함수 내부에서만 활용하면 한계가 있다.
위 예제를 외부에서도 활용 가능할 수 있도록 아래처럼 바꿔보자.
function outer() {
var outerTit = '외부 함수';
return function inner() {
var innerTit = '내부 함수';
console.log(outerTit + ' ' + innerTit);
}
}
var func = outer();
console.log(func());
outer()
에서 존재하는 inner()
를 return
해주고, 외부에서 outer()
를 func
라는 변수에 담았다.
변수 func
를 이용하면 외부에서 outerTit
의 값을 출력 수 있게 된다.
외부에서 outer()
의 지역변수 outerTit
의 값을 얻을 수 있으나, 외부에서 임의로 outerTit
를 변경할 수 없다.
👉 외부함수outer()
에서 내부함수inner()
를 반환(return
)하고 외부에서 변수(func
)를 만들고 외부함수outer()
를 할당하면, 외부에서 외부함수outer()
의 지역변수(outerTit
) 값을 얻을 수 있다.
외부에서 임의로 outerTit
의 값을 변경하려면, outerTit
의 값을 외부에서 변경할 수 있도록 outer()
내에서 수단을 제공해줘야한다. 즉, 변경 권한을 줘야한다.
위 예제를 다시 한 번 아래처럼 바꿔보자.
function outer () {
var _outerTit = '외부 함수';
return {
get inner() { return _outerTitl },
set inner(innerTit) { _outerTit = innerTit; }
}
}
var func = outer();
func.inner = 10;
객체에 getter와 setter을 담아 return
하면, 권한을 부여 받았기 때문에 외부에서 _outerTit
을 변경할 수 있다.
내부에서 반환을 통해 권한을 주면, 외부에서 부여받은 권한을 이용해서 내부와 소통할 수 잇다.
지역변수 보호도 할 수 있다. 외부에 노출되지 않기 떄문에 getter와 setter의 설정을 어떻게 하냐에 따라 영향을 줄 수도 안 줄 수도 있다. 즉, 무엇을 반환할지는 outer()
에 달려있다.
function setName(name) {
return function() {
return name;
}
}
var sayMyName = setName("mook");
sayMyName(); // "mook"
전역 실행 컨텍스트가 생성되면서,
sayName
실행 컨텍스트 생성)setName
함수와 변수가 선언되고 setName("mook")
을 호출한다.sayName
실행 컨텍스트 종료name
을 선언한 후 "mook"
을 할당하고 익명함수를 선언하여 반환한다.sayMyName
실행 컨텍스트 생성sayMyName
에 할당하고 sayMyName
을 호출한다.sayMyName
실행 컨텍스트 종료name
을 탐색하고 없으면 상위로 올라가 sayName
에서 name
을 탐색한 후 "mook"
을 반환한다.function setCounter() {
var count = 0;
return function() {
return ++count;
}
}
var count = setCounter();
count(); // "mook"
setCounter
정의 후 실행한다.setCounter
스코프에 count
변수 선언 및 0
할당한다.count
에 할당하고 count
실행한다.count
을 탐색 후 없으면 상위의 setCounter
스코프에서 count
을 탐색한다.count
에 1을 증가시킨 값을 반환한다.Private Variable
자바스크립트는 비공개 선언을 제공하지 않기 때문에 클로저를 활용해 흉내낼 수 있다.
소프트웨어가 커지는 과정에서 어떠한 정보가 있을 때, 아무나 수정을 할 수 없게 방지한다.
TODO
1. 차량별로 연료량 및 연비는 랜덤
2. 유저별로 차량 하나씩 고르면 게임 시작
3. 각 유저는 자신의 턴에 주사위를 굴려 랜덤하게 나온 숫자만큼 이동
4. 만약 연료가 부족하면 이동 불가
5. 가장 멀리 간 사람이 승리
var car = {
fuel: 10, // 연료 (l)
power: 2, // 연비 (km / l)
total: 0,
run: function(km) {
var wasteFuel = km / this.power;
if(this.fuel < wasteFuel) {
console.log("이동 불가");
return;
}
this.fuel -= wasteFuel;
this.total += km;
}
}
이렇게 사용하게 되면 외부에서 프로퍼티 값을 마음대로 변경할 수 있다.
직접 변경할 수 없도록 하기 위해 스코프를 활용하여 위 코드를 변경할 수 있다.
var createCar = function(f, p) {
var fuel = f;
var power = p;
var total = 0;
return {
run: function(km) {
var wasteFuel = km / this.power;
if(this.fuel < wasteFuel) {
console.log("이동 불가");
return;
}
fuel -= wasteFuel;
total += km;
}
}
}
var car = createCar(10, 2);
이렇게 변경이 되면 사용자가 아무리 프로퍼티를 변경해도 영향이 미치지 않는다.
사용자는 오직 run
이라는 메소드만 사용할 수 있다.
run: function(km) { ... }
을 set
으로 변경하여 작성한다면, 권한은 보다 더 직접적일 수도 있다.
set fuel(f) { fuel = f }, ...
이런 식으로 변경하게 되면
외부에서 직접 지역변수 car.fuel
의 값을 변경할 수 있다.
이처럼 return
할 객체에 어떤 내용을 담을 것인지에 따라 달라진다.
return
)한다.return
한 객체에 포한되지 않은 멤버들은 private하다.return
한 객체에 포함된 멤버들은 public하다.함수 내부에서 다시 함수를 반환하면 반환된 함수는 최초 선언될 때 담고 있는 정보를 유지한다. 외부에 노출하고자 하는 데이터들만 반환하면 반환하지 않은 데이터들은 모두 외부에서 접근을 제한할 수 있다. 따라서 지역변수를 안전하게 보호할 수 있고, 외부에 공개되는 지역변수에 변경 권한을 부여하여 데이터를 활용할 수 있다.
⭐️ 클로저는 객체지향 프로그래밍과 함수형 프로그래밍의 currying을 위해 반드시 이해하고 알아야한다.
🔎 참고자료 🔎
- MDN
- 생활코딩
- PoiemaWeb
- javascript info
- 코어 자바스크립트
- 러닝 자바스크립트
- 인사이드 자바스크립트