타닥타닥 개발자의 일상

[JS/자바스크립트 ] Javascript JS closure의 사용예시로 closure 이해하기 본문

코딩 기록/JavaScript

[JS/자바스크립트 ] Javascript JS closure의 사용예시로 closure 이해하기

NomadHaven 2023. 10. 13. 16:44

자바스크립트의 closure란 개념은 무엇일까?

 

Mdn에서 clousre의 개념을 찾아보면 이렇다.

 

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

 

 

클로저(Closure)란 함수와 그 함수가 선언될 당시의 렉시컬 환경(Lexical Environment)과의 조합으로, 외부 함수의 스코프에 대한 접근을 내부 함수에서 가능하게 하는 것을 의미합니다. 다시 말해서, 클로저는 내부 함수에서 외부 함수의 범위에 접근할 수 있도록 해주는 것입니다. 자바스크립트에서는 함수가 생성될 때마다 클로저가 생성됩니다.

 

라는 gpt의 설명을 들어도 한번에 이해가 되지 않는다. 

 

누군가 closure를 이해하기 위해 좀더 쉬운 예시를 든적이 있다.

"closure는 어디든지 함수를 넣고 다니는 백팩이다. 이 백팩은 함수가 생성된 곳에서 보여지는 모든 변수가 담겨있다."

 

즉 closure란 기능을 통해서 함수의 변수에 접근할 수 있다는 말정도로 이해하면 될 듯하다.

 

그러면 clousre란 기능을 좀더 쉽게 이해하기 위해서 closure가 나타나는 코드를 살펴보도록 하자.

let f;

const g = function(){
  const a = 23;
  f = function(){
    console.log(a *2);
  }
}

const h = function(){
  const b = 777;
  f = function(){
    console.log(b *2);
  }
}


g();
f();
console.dir(f); 
h();
f(); 
console.dir(f);

위의 함수에서 g함수가 가장 첫번째로 호출 되었다.

g함수는 이미 호출되었고 callstack에서 사라졌지만, 두번째로 호출된 f함수의 출력 결과를 보면, 46이란 결과가 출력된다.

f함수에는 g함수의 변수 a에 2를 곱하는 함수가 대입되었다.

 

어떻게 f함수는 콜스택에서 사라진 g함수의 변수 a=23에 접근해서, 2를 곱하고 46이란 결과를 출력할 수 있었을까?

바로 위에서 언급한 closure 덕분이다.

 

이미 콜스택에서 g함수가 사라졌어도, closure를 통해서 g함수의 변수에 접근이 가능했기 때문에 a=23에 접근할 수 있었던 것이다.

 

세번째로 출력되는 console.dir(f) 를 출력해보면, 

f함수가 closure를 이용해 g함수의 변수 a에 접근했음을 알수 있다.

 

그 다음, 4번째로 h함수가 출력되었다. 이로 인해 f함수에는 h함수의 변수 b에 2를 곱하는 함수가 대입되었다.

그렇다면 5번째로 다시 f함수를 출력한다면 어떻게 될까? 이때도 당연히 closure가 적용된다.

이미 h함수가 콜스택에서 사라졌지만, closure를 통해서 h함수의 변수 b=777에 접근이 가능했으며, 777에 2를 곱한 1544가 출력된다.

 

가장 마지막에 console.dir(f)를 다시 출력해보면, 이전과 달리 closoure가 h함수에 적용된 것을 확인 할 수 있다.

이를 통해 리턴값이 없는 함수를 할당해도 closure가 적용되며, 재할당 받은 함수에도 closure가 적용됨을 알 수 있다.

 

 

또다른 코드를 통해 closure의 개념을 이해해보자.

 

const boardPassengers = function(n, wait){
  const perGroup = n / 3 ;
  
  setTimeout(function(){
    console.log(`We are now boarding all ${n} passengers`);
    console.log(`There are 3 groups, each with ${perGroup} passenger`);
  }, wait*1000) 

  console.log(`Will start boarding in ${wait} seconds`);
};

const perGroup = 1000;
boardPassengers(180, 3)

위의 코드의 특이한 점은, boadrPssengers 함수 내에서 선언된 perGroup 변수를, 함수 밖에서 새로운 수로 대입한 다음,

boardPassengers함수에 대입한 매개변수를 통해 다시 perGroup 변수에 수를 대입한다는 점이다.

 

또한 boardPassengers 내부에는 3초후에 실행되는 setTimeout 함수가 있다.

따라서 boardPassengers 가 호출되고 callstack에서 사라져도 setTimeout함수는 closure를 통해 boardPassengers의 변수에 접근할 수 있는 것이다.

 

위의 함수를 실행한 결과는 아래와 같다.

출력결과에서는 180/3을 한 60이 출력됨을 알 수 있다.

boardPassengers  함수를 선언하기 전에 perGroup에  = 1000을 대입했다고 해도,

boardPassengers  함수를 선언하면서 대입한 매개변수가 다시 perGroup 변수에 덮어씌워지는 점을 알 수 있다.

 

이를 통해서 n,wait과 매개변수에도 접근 할 수 있음을 알 수 있다. 왜냐면 매개변수 역시 함수의 지역 변수이므로, closure가 접근 가능한 변수이기 때문이다.

Comments