자바스크립트 엔진은 자바스크립트 코드를 실행시키는 프로그램이다.
브라우저들은 각각마다 자바스크립트 엔진을 갖고 있는데 가장 잘 알려진 엔진은 구글의 Chrome과 Node.js에 사용되는 V8엔진 이다.
V8엔진은 크롬의 자바스크립트 코드와 Node.js의 서버사이드 자바스크립트 코드를 실행하게 해준다.
자바스크립트 엔진 안에는
Call Stack과 Heap 영역이 있다.
Call Stack은 개발자가 작성한 코드가 실행되는 곳이다.
Heap은 우리가 작성한 어플리케이션이 필요한 Object를 저장하고 있는 곳 이다.
여기서 우리의 코드는 어떻게 기계어로 변환 될까?
우리는 우선 Compile 과 Interpretation의 차이부터 알아보자
Compilation
모든 코드가 컴파일러에 의해 한꺼번에 기계어로 변환되고, 컴퓨터가 실행할 수 있는 바이너리 파일로 작성된다.
Source code ---------------> Machine code -------------> Programming running
- compilation
- |binary file|
- Execution
이 방식의 장점은 컴파일 후 변환된 기계어를 실행시키므로 실행시간이 빠르다.
Interpretation
코드가 Interpreter에 의해 한줄 한줄 실행된다. 소스코드에서 기계어로의 변환은 실행 바로 직전에 일어난다.
Source code---------------------------> Programming running
- Execution line by line
이 방식의 장점은 컴파일 시간이 없다는 것이다. 그 대신 실행시간이 늘어난다.
자바스크립트는 원래 Pure Interpreter 언어였다.
그런데 Interpret 언어의 문제는 실행시간이 compile 언어에 비해 너무 느리다는 것이었다.
이는 옛날에는 괜찮았지만 현재에 들어서 낮은 성능은 큰 문제가 되었다.
사용자가 구글 맵을 사용할 때 지도를 잡아당기는데 반응까지 매번 1초가 걸린다면 절대 받아들일 수 없을 것이다.
많은 사람들은 자바스크립트는 Interpret 언어라고 생각하지만 반은 맞고 반은 틀렸다.
왜냐하면 모던 자바스크립트 엔진은 compilation과 interpretation을 같이 사용하기 때문이다.
이를 Just-in-time compilation이라고 부른다.
JIT compilation
모든 코드가 한번에 기계어로 변환되고 바로 실행된다. 컴파일과 차이점은 바이너리파일이 생성되지 않는다.
Source code ---------------> Machine code -------------> Programming running
- compilation
- Execution
또 컴파일과 달리 실행은 기계어로 변환 직후에 바로 일어난다.
Parsing
자바스크립트코드를 자바스크립트 엔진이 parse 한다. (의미 있는 단위로 나누어서 읽어들인다)
코드는 AST(Abstract syntax tree)라는 자료구조로 파싱된다. 이 과정에서 문법오류를 검사한다.
(AST는 기계어를 생성하기 위해 사용된다)
Compilation
AST를 이용해 기계어를 생성한다.
Execution
생성된 기계어를 즉시 실행한다.
모던 자바스크립트엔진은 최적화 전략이 적용되어 있다.
이게 무슨 말이냐 하면, 자바스크립트엔진은 실행을 최대한 빨리 앞당기기 위해 처음에 최적화가 안된 기계어를 생성한다. 그리고 실행중에 코드가 최적화되고 다시 컴파일 되고 실행되고, 이 과정을 반복한다. (이 과정은 메인 쓰레드가 아닌 개발자가 접근할 수 없는 특별한 스레드에서 일어난다.)
이 반복동안 실행의 중단 없이 덜 최적화된 코드는 최적화된 코드로 대체된다.
이 프로세스가 V8같은 모던 엔진을 빠르게 만드는 것이다.
자바스크립트 Runtime은 자바스크립트를 실행시키기 위해 필요한 모든 것이 포함되 있는 환경이다.
웹 브라우저에서의 Runtime을 예로 들면
- 자바스크립트 엔진
- Web APIs(브라우저에 의해 엔진에게 제공되는 기능들, Web APIs는 엔진이 global window object를 통해 접근이 가능할 뿐 자바스크립트 자체는 아니다.)
- Callback Queue(callback 함수들을 가지고 있는 곳이다.)
예를 들어 클릭 이벤트가 발생한다고 하자.
이에 클릭이벤트를 핸들링하는 클릭이벤트 핸들러가 있을 것이다.
Callback queue에 있는 핸들러가 호출되고, Call Stack이 비었을 때 핸들러가 Call Stack으로 옮겨져 실행된다.
이 일련의 과정들은 Event loop에 의해 일어난다.
Event loop가 callback함수를 Callback queue에서 꺼내어 Call stack으로 옮긴다.