Closure
2024. 1. 2. 21:33ㆍStudy/JavaScript
Closure
상위 함수 보다 하위 함수가 더 오래 살아있는 경우를 closure라고 한다.
A closure is the combination of a function and the lexical environment within wiich that function was declared.
클로저는 어떤 함수와 해당 함수가 선언된 렉시컬 환경의 조합이다.
function getNumber(){
var number = 5;
function innerGetNumber(){
return number;
}
return innerGetNumber; // 함수를 실행하지 않고 함수 자체를 반환
}
const runner = getNumber(); // getNumber()를 호출
console.log(runner);
// [Function: innerGetNumber]
// innerGetNumber 함수를 실행하지 않고 함수 자체를 반환했기 때문에 Function이 찍힘
console.log(runner()); // runner()를 실행하면 innerGetNumber()를 실행하게 된다.
// 5
// runner()를 실행하면 5가 반환된다.
runner()를 실행한 상황, 그러니까 innerGetNumber()를 실행한 상황은 이미 getNumber()가 실행이 된 이후이다.
getNumber()의 Execution Context가 끝난 상황, getNumber()가 콜스텍에서 사라진 상황에서
runner()를 실행했다.
이런 경우가 바로 상위 함수보다 하위 함수가 더 오래 살아있는 경우, closure다.
Chrome 브라우저 개발자모드에서 해당 코드를 디버깅 해보면,
Call Stack에 실제로 getNumber()가 올라가지 않은 상태에서 innerGetNumber()가 올라가 있는걸 확인할 수 있다.
그리고 Scope에 실제로 Closure (getNumber)가 생성이 되어있다. 그 안에 number: 5가 세팅이 되어있다.
(렉시컬 스코프에 의해서 함수를 선언할때의 위치가 상위 스코프를 정하기 때문에 number: 5 를 가지고있다.)
상위 함수에서 하위 함수를 반환함으로써 상위 함수가 먼저 실행이 끝나고
하위 함수를 나중에 실행할 수 있는 기능이 closure이다.
closure 사용 사례
1. 반복적인 작업을 해야 할 때 : 데이터 캐싱
10 * 10 이라는 계산이 매우 오래 걸린다는 가정을 했을 때, cacheFunction()을 호출할 때마다 10 * 10 의 계산을 하게되면
반복적인 작업에 의해서 리소스를 많이 잡아먹게 된다.
function cacheFunction(newNumb){
var number = 10 * 10; // 이 계산이 매우 오래걸린다는 가정을 했을 때
return number * newNumb;
}
console.log(cacheFunction(10));
console.log(cacheFunction(20));
console.log(cacheFunction(30));
// cacheFunction() 을 호출할 때마다 매번 매우 오래걸리는 계산을 해야 함
이럴 때 cacheFunction() 함수 안에 클로저를 만들어 훨씬 효율적으로 함수를 작성할 수 있다.
function cacheFunction(){
var number = 10 * 10; // 이 계산이 매우 오래걸린다는 가정을 했을 때
function innerCacheFunction(newNumb){ // closure
return number * newNumb;
}
return innerCacheFunction; // innerCacheFunction 함수 자체를 반환
}
const runner2 = cacheFunction();
console.log(runner2(10));
console.log(runner2(20));
console.log(runner2(30));
// innerCacheFunction() 함수만 호출하게 되므로 10 * 10 의 계산은 딱 한 번만 하게 됨
// innerCacheFunction() 함수에서 10 * 10의 계산을 기억하고
2. 반복적으로 특정 값을 변환해야 할 때 : 데이터 캐싱
반복적으로 특정 값을 변환해야 할 때 사용한다.
아래 코드를 보면, 외부에서 number 값을 access 할 수 있는 방법이 존재하지 않는데,
increment 함수는 number 값을 기억하고 있기 때문에 아래와 같이 number++ 의 값 반환이 가능하다.
function cacheFunction2(){
var number = 99;
function increment(){
number++;
return number;
}
return increment;
}
const runner3 = cacheFunction2(); // increment함수를 반환
console.log(runner3()); // 100
console.log(runner3()); // 101
// 외부에서 number 값을 액세스할 수 있는 방법이 존재하지 않는데
// increment는 number 값을 기억하고 있기 때문에
// 100, 101, 102 ... 의 값을 얻을 수 있다.
3. 정보 은닉
자바스크립트에서는 private변수라는 개념이 생긴지 오래되지 않았다. ( # 키워드 )
옛날에는 아래와 같이 생성자 함수에서 this 키워드를 사용하지 않고 변수를 생성하여 메소드를 이용해 사용했다.
function Idol(name, year){
this.name = name;
var _year = year;
// this 키워드로 저장하지 않았기 때문에 객체의 프로퍼티로 액세스 할 수 없다.
// 메소드 안에서는 사용 가능하다.
this.sayNameAndYear = function(){
return `안녕하세요 저는 ${this.name}입니다. ${_year}에 태어났습니다.`;
}
}
const yuJin = new Idol('안유진', 2003);
console.log(yuJin.sayNameAndYear());
// 안녕하세요 저는 안유진입니다. 2003에 태어났습니다.
// 객체의 메소드로 _year 변수에 접근하여 사용 가능
console.log(yuJin.name);
// 안유진
console.log(yuJin._year);
// undefined
// 객체의 프로퍼티로 액세스 불가능
'Study > JavaScript' 카테고리의 다른 글
Callback Hell and Promise (0) | 2024.01.04 |
---|---|
Async Programming (0) | 2024.01.04 |
Execution Context (1) | 2023.12.27 |
this (0) | 2023.12.27 |
Scope (0) | 2023.12.26 |