JavaScript/JS를 파헤쳐보자

Call Stack과 클로저함수

동구름이 2024. 7. 24. 23:34

Call Stack

콜 스택이란 무엇일까. Call Stack은 함수를 사용할 때마다, 관련된 정보들이 쌓이는 곳이다.

 

단어를 그대로 보면, Call 은 호출을 말하고 Stack은 자료 구조인 Stack을 말한다. 즉 함수가 호출될 때 스택에 정보가 쌓이는 곳을 콜스택이라고 한다.

 

 

프로그램에서 함수들이 연쇄적으로 호출될 때, 스택의 형태로 메모리에 들어왔다가 나가게 된다.

function f1() {
  f2();
}

function f2() {
  f3();
}

function f3() {

}

f1();

 

아주 간단한 예시로 살펴보자

 

위 프로그램을 실행시키면 어떻게 될까?

 스택에 위의 형태로 쌓이게 된다. f1()이 실행되며 먼저 쌓이고, 2 3 이 순서대로 들어온다.

 

 

f3()이 실행이 완료가 되어야 f2()의 동작이 끝낼 수 있고, f2()의 실행이 완료되어야 f1()의 동작을 끝낼 수 있다. 이것은 스택에서 pop을 하는 것과 똑같은 메커니즘으로 해석할 수 있을 것 같다.

 

 

클로저

클로저란 무엇일까. 이것은 콜스택의 개념과 관련이 깊다.

 

아래 예시를 살펴보자.

function createCounter() {
  let count = 0;
  return function() {
    count++;
    return count;
  };
}

const counter = createCounter();

console.log(counter());
console.log(counter());

 

앞서 살펴본 콜스택과 같이, createCounter() 메서드가 호출되고 종료되면 함수의 지역 변수, 매개 변수들이 스택에서 제거된다. 메모리에서 사라지게 되는 것이다.

 

 

그런데 위 코드에는 메모리에서 사라지지 않는 이유가 한 가지 있다.

//function createCounter() {
//  let count = 0;
  	return function() {
    	count++;
    	return count;
//  	};
//}

const counter = createCounter();

console.log(counter()); // 1
console.log(counter()); // 2

 

바로 counter에서 createCounter() 의 리턴 값인 function을 가지고 있기 때문이다.

 

 

그리고, function 내에서는 count라는 변수가 저장되어있기 때문에, createCounter()가 종료되었고 스택에서도 제거되었지만, 내부 변수는 힙 영역에서 계속해서 참조를 하게 된다.

 

그래서 console.log 를 찍으면 결과값은 count에 더해져서 출력된다.

const counter = null

그러다 만약 이렇게 counter가 해제되면 heap 영역의 내부변수는 더이상 스택에서 참조하는 값이 없기 때문에, 가비지 컬렉터의 대상이 된다.

 

 

실제 예시를 하나 살펴보자

function showLogAfterOneMinute(message, time) {
  
  setTimeout(function() {
    console.log(message);
  }, time);

}

showLogAfterOneMinute("1초뒤에 출력", 1000);

 

showLogAfterOneMinute() 메서드는 호출이 끝나면 스택에서 사라진다.

 

 

 

//function showLogAfterOneMinute(message, time) {

  setTimeout(function() {
    console.log(message);
  }, time);
//}

//showLogAfterOneMinute("1초뒤에 출력", 1000);

그런데 1초 뒤에는 안의 메시지 값이 출력이 된다.

 

클로저에 의해 매개변수가 메모리 상에 유지되어있기 때문에 가능한 일이다.