Javacsript si Wierd - 2-1. Compiler, Engine, Scope-thumbnail

Javacsript si Wierd - 2-1. Compiler, Engine, Scope

What is scope? with engine and compiler
449

Compiler, Engine, Scope

이번 글에서는 Javascript에서 Compiler, Engine, Scope가 무슨 녀석들이고 어떤 관계인지에 대해서 간단하게 정리하고 넘어가자. 간만에 코드가 적고 줄글이 길어질 예정이다. Compiler, Engine, Scope가 어떤 녀석들인지 궁금하지 않다면 건너뛰어도 좋다.

아래와 같은 코드가 있다고 가정해보자.

var a = 1;

Compiler: global "Scope"야, 혹시 variable a가 네 collection 안에 존재하니?
Scope: 아니.
Compiler: 좋아, variable a를 선언할게. (선언함)
Compiler: 나중에 a = 1"Engine"이 실행할 수 있도록 "Engine"이 이해할 수 있는 코드를 만들어둬야지. (만듦)
Engine: a = 1을 해야하네? global "Scope"야, 혹시 LHS 검색을 해야 하는데 도와줄래? a라는 variable이 너에게 존재하니?
Scope: 방금 전에 "Compiler"가 선언해두었어.
Engine: 좋아, 거기에 1을 할당해야겠다. (할당함)

여기서 잠시 LHS와 RHS에 대해 짚고 넘어가자. Left와 Right의 차이인데, 사실 위치로 기억하는 것보다는, LHS는 값을 넣거나 변경하는 경우 사용되고, RHS는 값을 불러와서 사용할 때 쓰인다고 보면 된다. 아래의 과정을 보며 Compiler, Engin, Scope의 관계, 그리고 LHS와 RHS에 대해서 더 자세히 알아보자.

function fn(param1) {
  console.log(param1);
}

fn(1);

Compiler: global "Scope"야, 혹시 function fn이 네 collection 안에 존재하니?
Scope: 아니.
Compiler: 좋아, function fn을 선언할게. (선언함) function이니까 할당까지 할게 (할당함)
Engine: fn에 대한 RHS 검색이 필요해. fn을 사용해야 하거든. global "Scope"fn 아니?
Scope: 방금 전에 "Compiler"가 선언하고 할당해두었어.
Engine: 좋아 fn을 실행할게. (실행함)
Engine: global "Scope"야 param1에 대한 LHS 참조가 필요하거든? 1을 거기에 할당해야 해서 그래. 혹시 param1이라고 아니?
Scope: 방금 전에 "Compiler"가 fn의 parameter로 선언했어.
Engine: 좋아 거기에 1을 할당해야겠어. (할당함)
Engine: global "Scope"야 혹시 console 아니? RHS 참조가 필요한걸. 실행해야 해서 그래.
Scope: 내장된 메서드야. 여기.
Engine: 고마워 console.log를 실행할게. (실행함)
Engine: global "Scope"야, 그 혹시 param1은 알고 있니? console.log()에 인자로 전해줘야 해서 RHS 검색이 필요해.
Scope: 그럼, 아까 우리가 할당했잖아.
Engine: 그렇구나. 1이네. (console.log에 넘겨줌)

이런 과정을 거치다보니 Compiler든 Engine이든 다 Scope에게 질문을 거는 관계를 맺게 된다. 따라서 중첩된 scope를 가진 코드가 실행될 경우, 하위 scope에서 찾지 못한 변수에 대해서 최상단의 scope들에게 묻는 과정이 추가된다. 이것까지 예시를 들었다간 대화문만 엄청 길어질 것 같아서 이 정도로만 하자.

참고로 LHS와 RHS는 서로 각기 다른 error를 던진다는 점에서도 차이를 확인할 수 있다.

function fn(a) {
  console.log(a, b);
}

fn(1); // Uncaught ReferenceError: b is not defined

위의 경우 error가 나는 부분은 b의 RHS 참조가 필요한 부분인데, RHS 참조가 아예 불가능한 상황인 경우 ReferenceError가 발생한다.

function fn2(a) {
  b = a;
}

fn2(1);
b; // 1

위의 경우 error가 나(야 하)는 부분은 b의 LHS 참조가 필요한 부분이다. 하지만 Strict Mode로 실행하지 않을 경우 ScopeEngine이 찾는 이 variable을 그냥 생성해서 Engine에게 넘겨준다. 그러므로 error는 발생하지 않고, b는 1이라는 값을 갖게 되는 것을 확인할 수 있다. 물론 Strict Mode로 실행시 LHS는 RHS와 비슷하게 ReferenceError를 발생시킨다.

function fn3(a) {
  a();
}

fn3(1); // Uncaught TypeError: a is not a function

마지막으로 RHS 검색을 해서 값은 찾았는데, 해당 값으로 실행할 수 없는 일을 실행시키려고 할 때는 TypeError가 발생한다. 위의 예시에서는 Number를 가지고 Function처럼 쓰려고 했기 때문에 TypeError가 발생한 모습이다.


참고 서적 : 카일 심슨, 2017, 한빛미디어, 『YOU DON'T KNOW JS: 타입과 문법, 스코프와 클로저』