JavaScript 언더 더 후드

목차
- 스레드 및 호출 스택
- 실행 컨텍스트
- 이벤트 루프 및 비동기 JavaScript
- 메모리 저장소 및 가비지 컬렉션
- JIT(Just In Time) 컴파일
- 요약
이 기사에서는 JavaScript의 내부 작업과 실제로 실행되는 방식에 대해 자세히 설명합니다. 세부 사항을 이해하면 코드의 동작을 이해할 수 있으므로 더 나은 앱을 작성할 수 있습니다.
JavaScript는 다음과 같이 설명됩니다.
비차단 이벤트 루프가 있는 단일 스레드, 가비지 수집, 해석 또는 Just In Time 컴파일 프로그래밍 언어입니다.
이러한 핵심 용어를 각각 풀어 보겠습니다.
스레드 및 호출 스택:
JavaScript 엔진은 프로그램을 실행하는 데 사용되는 단일 호출 스택 과 힙으로 구성된 단일 스레드 인터프리터 입니다.
콜 스택 은 LIFO(Last In, First Out) 원칙을 사용하여 함수 호출(호출)을 임시로 저장하고 관리하는 데이터 구조입니다.
이는 스택에 마지막으로 푸시된 함수가 함수가 반환될 때 가장 먼저 팝아웃된다는 것을 의미합니다.
호출 스택이 단일이므로 함수 실행은 위에서 아래로 한 번에 하나씩 수행됩니다. 이는 호출 스택이 동기식임을 의미합니다.
이제 동기식이므로 JavaScript가 비동기식 호출을 어떻게 처리할 수 있는지 궁금할 것입니다.
음, 이벤트 루프는 JavaScript의 비동기 프로그래밍 뒤에 숨겨진 비밀입니다.
그러나 JavaScript 내에서 비동기 호출의 개념과 단일 스레드 언어로 이것이 어떻게 가능한지 설명하기 전에 먼저 코드가 실행되는 방법을 이해하겠습니다.
실행 컨텍스트(EC):
실행 컨텍스트는 JavaScript 코드가 실행되는 환경으로 정의됩니다.
실행 컨텍스트 생성은 두 단계로 이루어집니다.
1. 메모리 생성 단계:
- 전역 개체(브라우저에서는 창 개체, NodeJS에서는 전역 개체라고 함)를 만듭니다.
- "this" 개체를 만들고 전역 개체에 바인딩합니다.
- 변수 및 함수 참조를 저장하기 위한 메모리 힙 설정(힙은 대부분 구조화되지 않은 대규모 메모리 영역).
- Hoisting 을 구현하여 전역 실행 컨텍스트에 함수 및 변수를 저장합니다 .
코드 실행 이면의 단계를 알았으니 이제
이벤트 루프:
먼저 이 다이어그램부터 살펴보겠습니다.

두 가지 주요 구성 요소로 구성된 엔진이 있습니다.
* 메모리 힙 — 메모리 할당이 발생하는 곳입니다.
* 호출 스택 — 코드가 실행될 때 스택 프레임이 있는 곳입니다.
액세스할 수 없는 스레드인 Web API가 있으므로 호출만 하면 됩니다. DOM, AJAX, setTimeout 등과 같이 동시성이 시작되는 브라우저의 일부입니다.
마지막으로 처리할 이벤트 목록인 콜백 큐가 있습니다. 각 이벤트에는 이를 처리하기 위해 호출되는 관련 함수가 있습니다.
그렇다면 여기서 이벤트 루프의 작업은 무엇입니까?
이벤트 루프에는 콜 스택과 콜백 큐를 모니터링하는 간단한 작업이 하나 있습니다. 콜 스택이 비어 있는 경우 이벤트 루프는 큐에서 첫 번째 이벤트를 가져와 효과적으로 실행하는 콜 스택으로 푸시합니다.
이러한 반복을 이벤트 루프에서는 틱이라고 합니다. 각 이벤트는 함수 콜백일 뿐입니다.
메모리 저장소 및 가비지 컬렉션:
가비지 수집의 필요성을 이해하려면 먼저 모든 프로그래밍 언어에서 거의 동일한 메모리 수명 주기를 이해해야 합니다. 여기에는 3가지 주요 단계가 있습니다.
1. 메모리를 할당합니다.
2. 할당된 메모리를 사용하여 읽거나 쓰거나 둘 다 사용합니다.
3. 더 이상 필요하지 않으면 할당된 메모리를 해제합니다.
대부분의 메모리 관리 문제는 할당된 메모리를 해제하려고 할 때 발생합니다. 발생하는 주요 관심사는 사용되지 않는 메모리 리소스를 결정하는 것입니다.
메모리가 더 이상 필요하지 않을 때 개발자가 수동으로 결정해야 하는 저수준 언어의 경우 JavaScript와 같은 고급 언어는 가비지 수집(GC)이라는 자동화된 형태의 메모리 관리를 사용합니다.
JavaScript는 GC를 수행하기 위해 두 가지 유명한 전략인 참조 카운팅 기술과 Mark-and-sweep 알고리즘을 사용합니다. 다음 은 알고리즘과 작동 방식에 대한 MDN
의 자세한 설명입니다 .
JIT(Just In Time) 컴파일:
JavaScript 정의로 돌아가 보겠습니다. "해석되고 JIT 컴파일된 프로그래밍 언어"라고 되어 있는데 그게 무슨 뜻인가요? 일반적으로 컴파일러와 인터프리터의 차이점부터 시작하는 것은 어떻습니까?
비유로서 의사소통을 원하는 다른 언어를 사용하는 두 사람을 생각해 보십시오. 컴파일은 언어를 배우기 위해 멈춰서 전체 시간을 보내는 것과 같으며, 통역은 각 문장을 해석할 누군가가 있는 것과 같습니다.
따라서 컴파일 언어는 쓰기 시간이 느리고 실행 시간이 빠르며 해석 언어는 그 반대입니다.
기술 용어로 말하자면, 컴파일은 프로그램 소스 코드를 실행하기 전에 기계가 읽을 수 있는 이진 코드로 변환하는 프로세스이며 컴파일러는 전체 프로그램을 한 번에 처리합니다.
반면 인터프리터는 프로그램 명령을 기계가 읽을 수 있는 형식으로 사전 컴파일하지 않고도 실행하는 프로그램이며 한 번에 한 줄의 코드를 사용합니다.
그리고 해석된 프로그램의 성능을 향상시키는 JIT 컴파일 역할이 있습니다. 전체 코드는 한 번에 기계어 코드로 변환된 후 즉시 실행 됩니다.

JIT 컴파일러 내부에는 모니터(프로파일러라고도 함)라는 새 구성 요소가 있습니다. 그 모니터는 실행되는 코드를 감시하고
- 코드의 핫 또는 웜 구성 요소를 식별합니다(예: 반복 코드).
- 런타임 중에 해당 구성 요소를 기계 코드로 변환합니다.
- 생성된 기계 코드를 최적화합니다.
- 코드의 이전 구현을 핫스왑합니다.
이제 핵심 개념을 이해했으므로 잠시 시간을 내어 모든 것을 종합하고 코드를 실행하는 동안 JS 엔진 이 따르는 단계를 요약해 보겠습니다.

- JS 엔진은 사람이 읽을 수 있는 구문으로 작성된 JS 코드를 기계어 코드로 변환합니다.
- 엔진은 파서를 사용하여 코드를 한 줄씩 살펴보고 구문이 올바른지 확인합니다. 오류가 있으면 코드 실행이 중지되고 오류가 발생합니다.
- 모든 검사가 통과되면 파서는 추상 구문 트리(AST)라는 트리 데이터 구조를 생성합니다.
- AST는 코드를 트리와 같은 구조로 나타내는 데이터 구조입니다. 코드를 AST에서 기계 코드로 변환하는 것이 더 쉽습니다.
- 그런 다음 인터프리터는 AST를 가져와 기계 코드의 추상화이자 JS 코드와 기계 코드 사이의 중개자인 IR로 변환합니다. IR은 또한 최적화를 수행할 수 있으며 더 이동성이 뛰어납니다.
- 그런 다음 JIT 컴파일러는 생성된 IR을 가져와서 코드를 컴파일하고 즉시 피드백을 받고 해당 피드백을 사용하여 컴파일 프로세스를 개선함으로써 기계 코드로 변환합니다.
읽어 주셔서 감사합니다 :)
Twitter 및 LinkedIn 에서 저를 팔로우할 수 있습니다 .