프로그램의 평가와 실행 과정(2)

Doum Kim·2020년 6월 25일
0

Javascript

목록 보기
18/23
post-thumbnail

프로그램 실행과 실행 문맥

실행 문맥은 스택이라는 구조로 관리된다. 스택이란 일종의 자료 구조로 데이터를 아래에서 위로 쌓아 올려 마지막으로
추가한 데이터를 먼저 꺼내는 후입 선출 방식으로 관리된다. 배열의 push, pop 메소드를 생각하면 이해하기 쉽다.

항상 전역 컨텍스트가 스택의 가장 아래 차지하고 있고 전역 코드 안에서 함수를 호출하면 함수를 실행하기 위한 실행 문맥을 스택에 push한다. 그리고 함수가 종료되면 스택에서 pop된다. 이러한 실행 문맥 스택을 호출 스택(call stack)이라고 한다.

let person = "harin";
const print = () => {
  let person2 = "jay";
  const innerPrint = () => {
    console.log(person);
    console.log(person2);
  }
  innerPrint();
  console.log("print finished");
}
print();
console.log("finished");
//'harin'
//'jay'
//"print finished"
//"finished"

전역 실행 컨텍스트가 생성되고 환경 레코드에 전역 변수 person과 print 함수가 기록이 된다.
그 후에 print 함수가 호출이 되면서 print 실행 컨텍스트가 생성되고 환경 레코드에 person2 변수와 innerPrint 함수가 기록된다. 외부 렉시컬 환경 참조에는 전역 환경의 참조가 들어있다. 또 inner 함수가 호출이되면 외부 렉시컬 환경 참조에는 print 실행 컨텍스트의 렉시컬 환경 참조가 들어간다. 그리고 innerPrint의 console이 다 실행되면 inner 실행 컨텍스트는 call stack에서 pop된다. 그리고 print 실행 컨택스트도 console.log("print finished"); 구문을 마지막으로 call stack에서 pop된다.

여기서 중요한 것은 외부 렉시컬 환경 참조라는 컴포넌트를 이용해서 전역 컨텍스트까지 계속 타고 올라가는 체인이 형성되는데 그것을 스코프 체인이라고 부른다.
스코프 체인이 있기 때문에 내부 함수에서 외부 함수의 변수에 접근할 수 있다.

클로저

자바스크립트를 처음 공부했을 때는 이보다 어려운 개념은 없었다.
하지만 지금은 실행 컨텍스트를 완벽히 이해하였기 때문에 사실 클로저를 따로 정리할 생각도 들지 않는다.

정말 간단하다.

예로 makeCounter라는 함수가 있고 그 내부에는 count 변수와 increaseCount함수가 있다.
따라서 이 makeCounter를 호출하면 makeCounter 컨텍스트가 생성이 되고 렉시컬 환경에는 count 변수와 increaseCount 함수가 추가가 될 것이다. 그리고 increaseCount 함수를 반환을 해서 counter 변수에 할당해준다.
그렇게 되면 가비지 컬렉션이 동작하지 않고 생성된 렉시컬 환경 컴포넌트를 계속 사용할 수 있는 것이다.

여기서 중요한건 클로저는 그냥 단순 함수가 아닌 내부에서 선언된 함수와 렉시컬 환경 컴포넌트의 집합이라는 점이다.

또한 이 내부함수가 선언되는 그 시점을 알고 있으면 렉시컬 컴포넌트에 어떤 값들이 들어가 있는지 쉽게 알 수 있다.

결론적으로는 이런 클로저라는 특징을 이용해 변수를 은닉하여 지속성을 보장하는 등의 다양한 기능을 구현할 수 있다.

const makeCounter = () => {
  let count = 0;
  const increaseCount = () => {
    return count++;
  };
  return increaseCount;
};

const counter = makeCounter();

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

0개의 댓글