20211004 TIL

Breadman·2021년 10월 4일
0

TIL

목록 보기
6/11
post-thumbnail

v8이 어떻게 내 코드를 읽는가?

지난번에 진행했던 모의 면접의 꼬리 질문으로,

"var는 함수 스코프라고 했는데, 그럼 전역에서 선언하면 못읽어야하지 않나?"

가 있었다.

답변을 하기 위해 짧은 시간동안 머릿속을 굴려봤지만, 내 추측일 뿐 명확한 답이 아니었기에 답변하지 못했다.

C나 C++는 기본적으로 main 함수를 실행하면서 동작하는 걸 알고 있었다. 따라서 javascript도 크게 보면 함수로서 코드를 실행하는 게 아닐까하는 생각으로 v8이 어떻게 코드를 실행하는지에 대해 찾아봤다.

간단히 정리하면,
1. parser를 거쳐 AST(Abstract Syntax Tree)(추상 구문 트리)가 생성되고
2. 생성된 AST는 ignition이라는 인터프리터를 통해 bytecode로 변환된다.
3. 그리고 TurboFan이라는 컴파일러를 통해 최적화된 bytecode를 만들어낸다.
4. 그리고 실행..

AST는 컴퓨터가 해석하기 쉽도록 구조화된 Tree를 말하는 것 같다.
(타입이 어떻고, 이름이 어떻고, 인자는 뭐가 들어가고...)

ignition을 통해 bytecode로 변환한다고 하는데, bytecode는 컴퓨터 친화적인 코드라고 한다.
(마치 어셈블리같이 레지스터에 넣고, 더하고, 지지고, 볶는 과정을 거친다.)
ignition이 인터프리트 방식을 사용하는 이유는 javascript 컨셉상 타입이 정적인 c와 다르고 동적이기 때문에, 코드 한줄을 실행하면서 최적화를 하기 위함이라는 것 같다..
(장점으로 '메모리 사용량 감소', '파싱 시 오버헤드 감소', '컴파일 파이프라인의 복잡성 감소'가 있다는데, 아직 와닿지 않는다..)

TurboFan는 컴파일하는 과정에서 여러 최적화 기법을 이용해 자주 사용되거나, 작고 단순한 코드들을 최적화한다. 대표적인 최적화기법으로 히든 클래스와 인라인 캐싱이 있다고 한다.
(이 부분은 다른 개념을 찾다가 본 익숙한 keyword라 따로 찾아봐야겠다.)

let, const도 hoisting 된다

console.log(name);
let name = 'breadman';

위 코드는 에러가 뜨기 때문에, 당연히 let은 hoisting이 안된다고 생각했다.
하지만 에러메세지를 보면 묘하다.

Uncaught ReferenceError: Cannot access 'name' before initialization

(크롬에선 not defined가 뜬다. node에서 재현할 수 있다.)
(왜 그런진 아직 모르것다..)
초기화되기 전에 접근할 수 없다는 메세지다.
위 코드에서 let name = 'breadman'; 을 지우고 실행하면, not defined 에러가 발생한다.
호이스팅이 되지 않았다면 not defined가 떠야 정상이라는 것이다.

(V8엔진의 코드를 까보면 HoistSloppyBlockFunctions라는 함수 내부에 should_hoisting이라는 변수가 있는데, 해당 변수는 선언과 동시에 true로 초기화되고 있다.)
(무조건 "true로 초기화되니까 호이스팅되고 있다!"고 말할 순 없을 것 같다. 코드의 흐름을 봐야 정확히 알 수 있을 것 같다.)

  • 추가로 아래의 코드는 웹브라우저에서도 정상동작함으로서 호이스팅이 된다는 걸 증명할 수 있다.
function sayHi() {
    console.log(a,b);
}
let a = 'hi'
const b = 'hello';
sayHi();	// hi hello
profile
빵돌입니다. 빵 좋아합니다.

0개의 댓글