About Javscript

개요

모두 알다시피 javascript는 인터프리터 언어이다. 1995년 자바스크립트는 유저와의 상호작용을 위해 10일만에 탄생한 언어이고, "HTML 페이지를 동적으로 만드는 것"에 목적이 있어 가벼운 인터프리터 언어로 만들어졌다.


Compile 언어와 Interpreter 언어

컴파일 언어와 인터프리어 언어의 가장 큰 차이점은 pre-processing, 컴파일 유무다.

컴파일은 고급언어로 작성된 코드를 기계어로 변환하는 과정을 말하는데, 당연히 변환된 기계어를 실행하기 때문에 빠를 수 밖에 없다.

function sum () {
   let result = 0
   for (let i = 1 ; i <= 10 ; i++){
      result += i;
   }
   return result;
}
sum() // for loop.. -> 55
sum() // for loop.. -> 55
sum() // for loop.. -> 55

// compile 결과
sum() = 55
sum() = 55
sum() = 55

컴파일을 마친 기계어는 실행만 하면 되지만, 인터프리터 언어는 매번 sum 함수를 실행하여 loop를 돌게 된다.

Javascript 생태계 에서 유명한 컴파일로는 Babel과 Typescript가 있다.

Babel은 ES6등 최신 자바스크립트 스펙을 모든 브라우저에서 사용 가능한 스크립트로 컴파일한다.

Typescript는 TS로 작성된 코드를 JS로 변환한다.

그리고 결국 Javascript도 실행되기 위해 컴파일 과정을 거친다.

다만 자바스크립트 엔진 내부에서 실행 중 컴파일이 필요한 경우에 내부에서 컴파일 한다.


Javascript Engine

자바스크립트 엔진은 자바스크립트 코드를 해석하고 실행하는 인터프리터이다.

V8엔진이 가장 대중적으로 알려져있다. (Node.js와 Chrome에서 사용되기 때문)

Chrome : V8

Safari : Webkit

FireFox : SpiderMonkey

Edge : Chakra

우선 V8엔진이 자바스크립트의 성능을 비약적으로 향상시킬 수 있었던 이유에 대해 알아보자.

엔진이 작동하는 원리는 이렇다. 먼저 엔진이 실행할 JS 파일을 받게 된다.

파싱, AST(Abstract Syntax Tree)를 구축하는 과정을 거친다.

다음으로 Interpreter가 코드를 읽으며 실행한다.

코드를 수행하는 과정에서 프로파일러가 지켜보며 최적화 할 수 있는 코드를 컴파일러에게 전달해준다. 주로 반복해서 실행되는 코드 블록을 컴파일(최적화)한다.

그리고 원래 있던 코드와 최적화된 코드를 바꿔준다.

코드를 우선 인터프리터 방식으로 실행하고 필요할 때 컴파일 하는 방법을 JIT(Just-In-Time) 컴파일러 라고 부른다.

크롬의 V8 엔진을 포함해 Mozilla의 Rhino, Firefox의 SpiderMonkey도 같은 방법을 사용한다.

결론은, 자바스크립트는 실행되는 플랫폼에 따라 인터프리팅과 컴파일이 혼합되어 사용된다.

이 방식은 자바스크립트의 성능을 크게 향상시켰다.


Call Stack & Memory Heap

자바스크립트 엔진은 Memory Heap과 Call Stack으로 이루어져있다.

Memory Heap : 변수와 객체의 메모리 할당이 발생하는 곳

Call Stack : 코드가 실행될 때마다 호출 스택이 쌓이는 곳

자바스크립트 엔진은 현재 진행상황을 추적하기 위해 콜스택을 사용한다.

스택은 LIFO (후입선출) 형태로 대표적인 자료구조의 하나다. 아래 예시를 보며 콜스택의 동작과정을 이해해보자.

한가지 더 기억하자. 콜스택은 스코프 내부의 원시타입 변수를 저장한다.

원시타입 변수는 string, number 등 메모리 크기가 고정된 변수다.

원시타입과 참조타입을 잠깐 살펴보자.

const str = 'a'; // 1 byte
const str1 = 'aa'; // 1 * 2 = 2 byte
const arr = []; // ?

str 변수는 런타임에서 메모리의 크기가 바뀌지 않는다. 반면에 arr 변수는 런타임에 한개의 element 가 추가될수도, 100개가 추가될수도 있다. 반대로 지워질 수도 있다.

중요한 점은 해당 arr 변수의 크기가 유동적으로 변화한다는 점이다.

이렇게 원시타입 데이터는 콜스택에 저장된다. 아니라면, 메모리 힙에 저장된다.

C언어를 예시로 들자면 malloc을 해서 할당해주던, 동적으로 크기가 결정되는 데이터 타입을 메모리 힙에 저장한다는 얘기이다.

물론 C에서는 free를 통해 Memory Leak을 방지했지만, 자바스크립트 엔진의 Garbage Collector가 이 일을 대신할 것이다.


Runtime

자바스크립트 엔진에서 제공해주지는 않지만, 자바스크립트 개발자가 사용하는 setTimeout 과 같은 함수들이 있다.

이러한 함수들은 브라우저에서 제공하는 Web API이다. 그리고 이런 Web API의 호출을 제어하기 위한 Callback Queue와 Event Loop가 있다.

자바스크립트는 싱글 스레드 언어이기 때문에 한 번에 한 가지 작업만 실행한다. 콜 스택이란 프로그램에서 우리가 어디 있는지를 기록하는 자료 구조이다.

함수를 실행하면, 그 함수가 콜 스택의 가장 상단에 위치하게 된다. 그리고 함수의 실행이 종료되면 그 함수는 스택에서 제거된다.

바로 여기서 호출 스택의 각 항목을 스택 프레임이라고 한다.

그런데 호출 스택에 처리 시간이 굉장히 오래 걸리는 함수가 있으면 어떻게 될까?

자바스크립트는 싱글 스레드이기 때문에 함수가 종료될 때까지 다른 작업들이 모두 대기 상태가 될 것이다.

이러한 상황을 제어하기 위해 사용하는 것이 비동기 콜백(Asynchronous Callback)이다.

즉, 코드 일부를 실행하고 나중에 실행될 콜백 함수를 제공하는 것이다. 비동기 콜백은 즉시 콜스택에 올라가는 것이 아닌, 특수한 시점에 실행이 되므로 스택안에 바로 올라가지 않는다.

그리고 이 콜백 함수들의 스케줄을 관리하는 것이 Event Loop이다. 이벤트 루프는 콜 스택이 비어있는 것을 확인하면 콜백 큐에 대기중인 항목을 스택에 올린다.


Sources …

Last updated