개발/Javascript

[You don't know JS] Part2 - 1장. 스코프란 무엇인가

lanace 2020. 9. 6. 06:24

Ch1. 스코프란 무엇인가

프로그램의 기본 페러다임중 하나는 어떻게 변수를 저장하는지, 어떻게 저장된 값을 불러오는지 이다.

이런 기능은 프로그램에 상태를 부여하게 되고, 다양한 작업을 가능하게 해준다.

스코프란 어떻게 변수를 생성하는지,
그리고 어떻게 변수값을 불러오는지 정한 규칙이다.

1.1 컴파일러 이론


자바스크립트는 인터프리터 언어이다.

하지만 컴파일 과정을 수행한다.

실제 코드를 수행하기전에 3단계를 거치는 컴파일레이션을 한다

다른 언어와 다르게 자바스크립트의 컴파일 과정은 시간이 매우 짧다.

따라서 최적화 하는데 소요되는 시간도 짧게때문에 엄청난 최적화가 이루어지진 못한다.

이를 극복하기 위해서 엔진은 Lazy Compile 이나 hot recompile 같은 JITs를 사용한다.

1. Tokenizing / Lexing

문자열을 나누어 Token이라 불리는 의미있는 작은 조각을 만든다.

var a = 2;

위 코드에서의 토큰은 다음과 같다.

  • var
  • a
  • =
  • 2
  • ;

공백은 토큰이 될 수도 있고, 안될 수도 있다.
토큰의 기준은 의미가 있는지 없는지에 따라 다르다.

⇒ 그럼 자바스크립트에서 공백은 의미가 있는가? ⇒ 난 없는것같음

Tokenizing과 Lexing 은 미묘한 차이를 가지고 있다.
토큰을 인식할때 상태 유지 방식을 사용한다면 Lexing,
상태 없이 토큰을 인식한다면 Tokenizing 이라 한다.

상태 유지 방식은 해당 토큰이 문자열인지, 연산자인지, 이런것들을 구분하는것을 말하는듯 하다.
https://stackoverflow.com/a/380487

2. Parsing

Tokenizing 또는 Lexing 과정에서 만들어진 Token 배열을 가지고 문법 구조를 반영하여 트리 형태로 바꾸는 과정을 의미한다.

이렇게 생성된 트리는 AST(추상 구문 트리) 라 한다.

var a = 2; 의 트리는 먼저 변수 선언이라 부르는 최상위 노드에서 시작되고,

또한 엔진에서 불필요한 요소를 삭제하는 과정을 거쳐 최적화 한다.

3. Code generation

Parsing 과정에서 만들어진 AST를 기반으로 컴퓨터가 실행할 수 있는 기계어로 바꾸는 과정을 Code generation 단계라 한다.

여기선 타깃으로 하는 플랫폼에 따라 달라진다.

또한 엔진에서 불필요한 요소를 삭제하는 과정을 거쳐 최적화 한다.

코드 실행전엔 반드시 컴파일 되어야 되며, 보통 실행 직전에 컴파일 된다.

따라서 시간이 매우 짧으며, 이를 위해 레이지 컴파일이나 핫 리컴파일 같은 JITs 같은 기법이 이용된다.

레이지 컴파일과 리컴파일이 모지...?

1.2 스코프 이해하기


엔진

컴파일레이션의 시작과 끝

모든 자바스크립트 실행을 책임

컴파일러

파싱과 코드 생성의 작업을 진행

스코프

선언된 모든 변수를 찾을 수 있게 기록하고 관리한다.

엄격한 규칙을 강제하여 현재 실행 코드에서 접근 가능한지 확인

1.2.2 앞과 뒤

var a = 2;

위 코드의 컴파일 과정을 살펴보자.

위 코드는 아래 두가지로 나뉘어 본자.

  1. 컴파일러가 컴파일레이션 과정에서 처리할 구문
  2. 실행 과정에서 엔진이 처리할 구문

컴파일러가 렉싱을 통해 구문을 토큰으로 나눈다.

→ 토큰을 이용해 파싱 트리를 만든다.

→ 코드를 생성한다.

  1. 컴파일러가 var a를 만나 스코프에 a라는 변수가 있는지 확인하고, 있으면 선언부는 넘어가지만 없다면 새로 할당한다.
  2. 컴파일러가 a = 2; 를 실행하기 위해 스코프에 a 변수가 있는지 확인한다. 있으면 할당하고, 없다면 더 상위의 스코프를 찾는다.

만약 스코프에서 a라는 변수를 못찾으면 에러가 발생한다.

1.2.3 컴파일러체

LHS 검색

변수가 대입 연산자의 왼쪽에 있을때 수행

값을 넣어야 하므로 변수 컨테이너 자체를 찾는것

a = 2;

RHS 검색

변수가 대입 연산자의 오른쪽에 있을때 수행

단순 특정 변수의 값을 찾는것

console.log(a);

1.2.4 엔진과 스코프의 대화

1.2.5 퀴즈

function foo(a) {
    var b = a;
    return a + b;
}

var c = foo(2);
  1. 모든 LHS 검색 (3개)

    • foo 함수의 parameter a에 값 할당
    • b에다가 a를 넣기위해 b를 알려줘
    • c에 값을 할당
  2. 모든 RHS 검색 (4개)

    • foo의 값을 알아야겠어
    • var b = a; 에서 a의 값이 뭐지?
    • a + b를 하기 위해 a 값을 찾아줘
    • a + b를 하기 위해 b 값을 찾아줘

1.3 중첩 스코프


function foo(a) {
    console.log(a, b)
}

var b = 2;
foo(3);

스코프는 중첩될 수 있고, 만약 현재 스코프에 없다면 상위 스코프에서 찾는다.

최상위엔 글로벌 스코프가 존재한다.

글로벌 스코프에도 없으면 진짜 없는거다.

1.4 오류


LHS와 RHS의 중요한 차이는 찾지 못했을때 오류 여부이다.

RHS는 검색이 실패하면 다시는 값을 찾을 수 없다.

선언되지 않은 변수이기 때문이다.

이때 ReferenceError 가 발생한다.

LHS는 검색이 실패하면 새로운 변수를 알아서 생성해준다.

글로벌 스코프에 생성해준다.

strict mode에선 ReferenceError 가 발생한다.

만약 값을 가지고 불가능한 일을 하려고 한다면 TypeError가 발생한다.

스코프에서 검색은 성공했으나 할 수 없는일이기 때문이다.

1.5 정리하기


위에 잘 보자.