Closure

2024. 1. 2. 21:33Study/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