스코프와 클로저 - ChoDragon9/posts GitHub Wiki

스코프란 무엇인가

특정 장소에 변수를 저장하고 나중에 그 변수를 찾는 데는 잘 정의된 규칙이 필요하다. 이런 규칙을 스코프라고 한다.

1.1 컴파일러 이론

자바스크립트는 동적 또는 인터프리터 언어로 분류하나 사실은 컴파일러 언어이다. 전통적인 많은 컴파일러 언어처럼 코드를 미리 컴파일하거나 컴파일한 결과를 분산 시스템에서 이용할 수 있는 것은 아니다. 하지만 자바스크립트 엔진은 전통적인 컴파일러 언어에서 컴파일러가 하는 일의 상당 부분을 우리가 아는 것보다 세련된 방식으로 처리한다.

전통적인 컴파일러 언어의 처리과정은 3단계를 거치는 데, 이를 컴파일레이션(Compilation)이라고 한다.

토크나이징(Tokenizing) / 렉싱(Lexing)

문자열을 나누어 토큰이라 불리는 의미있는 조각으로 만드는 과정이다. var a =2;를 다음의 토큰으로 나눌 수 있다.

var,
a
=
2
;

토크나이징과 렉싱은 토큰을 인식할 때 무상태 방식으로 하는 지 상태 유지 방식으로 하는 지에 따라 구분한다. 토크나이저(Tokenizer)가 상태 유지 파싱 규칙을 적용해 a 변수가 별개의 토큰인지 다른 토큰의 일부인지를 파악한다면 렉싱이다.

파싱(Parsing)

토큰 배열을 프로그램의 문법 구조를 반영하여 중첩 원소를 갖는 트리 형태로 바꾸는 과정이다. 파싱의 결과로 만들어진 트리를 AST(Abstract Syntax Tree, 추상 구문 트리)라 부른다.

코드 생성(Code-Generation)

AST를 컴퓨터에서 실행 코드로 바꾸는 과정이다. 자바스크립트 엔진은 파싱과 코드 생성 과정에서 불필요한 요소를 삭제하는 과정을 거쳐 실행 시 성능을 최적화한다. 자바스크립트 엔진이 기존 컴파일러와 다른 점은 자바스크립트 컴파일레이션을 미리 수행하지 않아서 최적화할 시간이 많지 않다는 것이다.

자바스크립트 컴파일레이션은 보통 코드가 실행되기 겨우 수백만 분의 일초 전에 수행한다. 자바스크립트 엔진은 가능한 한 가장 빠른 성능을 내기 위해 이 책에서 다룰 범위를 가볍게 넘어서는 여러 종류의 트릭(레이지 컴파일이나 핫 리컴파일 같은 JITs)를 사용한다.

1.2 스코프 이해하기

1.2.3 컴파일러체

코드 생성한 코드를 실행할 때는 스코프에 검색을 한다. 단순히 특정 변수의 값을 찾는 검색은 RHS(Right-Hand Side)라고 불리고, 변수의 컨테이너 자체를 찾는 것은 LHS(Left-Hand Side)라고 불린다.

이때는 값을 찾으므로 RHS 검색이다.

console.log(a)

이때는 변수의 컨테이너를 찾으므로 LHS 검색이다.

var a = 2

1.4 오류

LHS와 RHS를 구분하는 이유는 두 종류의 검색 방식은 변수가 아직 선언되지 않았을 때 서로 다르게 동작하기 때문이다.

function foo(a) {
  console.log(a + b)
  b = a
}
foo(2)

b에 대한 RHS 검색이 실패하면 다시는 b를 찾을 수 없다. 이렇게 스코프에서 찾지 못하는 변수는 선언되지 않는 변수라 한다. RHS 검색이 중첩 스코프 안 어디에서도 변수를 찾지 못하면 엔진이 ReferenceError를 발생시킨다.

Uncaught ReferenceError: b is not defined

반면에, 엔진이 LHS 검색을 수행하여 변수를 찾지 못하면 글로번 스코프는 엔진이 검색하는 이름을 가진 새로운 변수를 생성해서 엔진에게 넘겨준다. Strict Mode에서는 다르게 작동한다. 글로벌 변수를 자동으로 또는 암시적으로 생성할 수 없기 때문에 RHS의 경우와 비슷하게 ReferenceError를 발생시킨다.