실행 컨텍스트란소스코드를 실행하는데 필요한 환경을 제공하고 코드의 실행 결과를 실제로 관리하는 영역
입니다.
실행 컨텍스트는 식별자를 등록하고 관리하는 렉시컬 환경의 스코프와, 코드 실행 순서 관리를 구현한 내부 메커니즘으로 모든 코드는 실행 컨텍스트를 통해 실행되고 관리됩니다.
실제로 실행컨텍스트 안에는 더 많은 부분이 있지만, 이 글에서는 렉시컬 환경만 설명하겠습니다.
우선 자바스크립트 엔진은 소스코드를 2개의 과정 소스코드의 평가
, 소스코드의 실행
으로 나누어 처리합니다.
소스코드의 평가
단계에서는 실행컨텍스트를 생성, 변수와 함수 선언문만 먼저 실행하여 변수나 함수 식별자를 실행 컨텍스트의 렉시컬환경안의 환경레코드에 등록
합니다.
실행컨텍스트 생성, 실행 컨텍스트 스택에 푸시
변수, 함수 등 식별자 등록을 평가단계에서 진행합니다.
소스코드의 실행
단계에서는 평가 단계에서 등록해둔 변수,함수등의 값을 바인딩
하고, 선언문 이외의 문을 실행
합니다.
런타임이 시작되어 선언문 외 코드를 순차적으로 실행합니다.
등록한 식별자에 값이 할당되고, 함수나 메서드가 호출됩니다.
시작하기에 앞서 어려운 단어들을 정리하고 가겠습니다.
렉시컬 환경
: 스코프와 식별자를 관리하는 곳으로, 환경레코드(EnvironmentRecord)
와 외부렉시컬 환경에 대한 참조(OuterLexicalEnvironmentReference)로
구성됩니다.
환경 레코드
: 스코프에 포함된 식별자를 등록하고, 식별자에 바인딩된 값을 객체형태로 관리하는 저장소 입니다. 소스코드의 타입에 따라 관리하는 내용에 차이가 있습니다.
외부 렉시컬 환경에 대한 참조(OuterLexicalEnvironmentReference)
: 해당 소스코드를 포함한 상위 코드의 렉시컬 환경인 외부 렉시컬 환경(상위 스코프)을 가리킵니다. 외부 렉시컬 환경에 대한 참조를 통해 단방향 링크드 리스트인 스코프 체인을 구현합니다.
호이스팅 : 호이스팅 글 참조
콜스택
: 코드 실행 시 생성되는 모든 실행 컨텍스트가 저장되는데 사용되며 이름에서 알 수 있듯 스택 자료구조로 되어있어 나중에 들어오는 실행 컨텍스트 부터 처리
를 합니다.
이때 최상단의 실행 컨텍스트는 언제나 현재 실행중인 코드의 실행 컨텍스트입니다.
전역 변수 및 전역함수의 선언문을 실행하여 식별자를 전역 실행 컨텍스트의 전역 스코프안에 식별자를 등록
합니다. 이때 var로 선언한 변수 및 함수
는 등록과 동시에 값을 undefined으로 초기화 합니다. 즉 변수 호이스팅 및 함수 호이스팅이 발생합니다.
변수 : var의 경우
선언과 동시에 undefined로 초기화 되지만,
let, const
로 선언한 변수들은 일시적 사각지대
에 있으므로 이 단계에서 호출을 한다면 참조에러가 발생
합니다.
함수 : 함수 표현식인 경우 변수 호이스팅, 함수 선언문인 경우 함수 호이스팅이 발생합니다.
전역 변수인 global 변수는 var로 선언했기 때문에 평가 단계에서 전역 스코프에 global 식별자를 등록하고, undefined로 값을 초기화 합니다.
함수 선언문인 firstDepth 함수 또한 등록합니다.
전역 소스코드가 실행이 되면, 전역변수에 값이 할당됩니다.(global=30)
선언문 이외의 문이 실행이 됩니다. 만약 함수가 존재하면 함수가 호출되고 콜스택에 호출된 함수에 관한 실행 컨텍스트가 쌓이게 되고
(firstDepth 실행 컨텍스트) 이후의 전역 코드의 실행은 일시 중단 되어 코드의 제어권이 호출된 함수(firstDepth)로 이동합니다.
firstDepth의 실행컨텍스트 안의 firstDepth 스코프에 변수 및 함수선언문을 실행하여 식별자를 등록 합니다.
first 식별자가 등록됩니다. 단 const로 선언했기 때문에 초기화는 되지 않습니다.
secondDepth를 등록합니다.
firstDepth 소스코드가 실행이 되면 first변수에 값이 할당됩니다.(first=40)
선언문 이외의 문이 실행이 됩니다. 만약 함수가 존재하면 함수가 호출되고, 콜스택에 호출된 함수에 관한 실행 컨텍스트가 쌓이게 됩니다.(secondDepth 실행 컨텍스트)
이때 firstDepth 코드의 실행은 일시 중단 되고 코드의 제어권이 호출된 함수(secondDepth)로 이동합니다.
second 식별자에 50의 값을 할당합니다.
함수나 메서드를 호출합니다.
이때 console.log(global)을 살펴보겠습니다. console 또한 함수 라서 실제로는 console관련 실행컨텍스트가 콜스택에 쌓이고, console 실행 컨텍스트 에서 console을 찾게 되지만 편의상 secondDepth에서 시작 하겠습니다.
console을 secondDepth 스코프에서 찾습니다.
console이 존재하지 않기 때문에 `Outer로 연결된 스코프 체인을 통해 상위 스코프인 firstDepth로 넘어갑니다.
firstDepth에 console이 존재하지 않기 때문에 Outer로 연결된 스코프 체인을 통해 상위 스코프인 전역으로 넘어갑니다.
전역객체에서 console을 찾을 수 있습니다.
console객체에 바인딩 된 log 메서드를 찾습니다.
이와 마찬가지로 인수인 global 또한 스코프체인으로 찾은뒤, 인수로 넘겨줍니다.
만약 찾으려는 식별자를 전역 객체까지 와서도 찾지 못했다면 없다고 판단합니다.
만약 실행할 코드가 남아 있다면 위의 과정을 반복해줍니다.
secondDepth코드에 더 이상 실행할 코드가 없다면 secondDepth 실행이 종료됩니다.
콜스택에서 secondDepth 실행 컨텍스트는 pop되어 제거되고, 제어권이 다시 firstDepth 로 넘어갑니다.
-------> secondDepth 함수 실행 종료
firstDepth로 돌아와 실행할 코드가 있다면 콜스택에 쌓고, 반복해줍니다. 만약 더 이상 실행할 코드가 없다면 firstDepth 실행이 종료됩니다.
콜스택에서 firstDepth 실행 컨텍스트는 pop되어 제거되고, 제어권이 전역으로 넘어갑니다.
-------> firstDepth 실행 종료
-------> 소스코드의 실행 끝