프로그래밍의 기본은 변수를 만들고 값을 저장해 필요할 때 가져다 사용하는 것이다.
그렇다면 변수는 어디에 저장이 되고 필요할 때 변수를 어떻게 찾을까?
프로그램은 특정 장소에 변수를 저장하고 나중에 그 변수를 찾는 규칙이 필요한데 이것을 스코프(Scope) 라고 한다.
그렇다면 스코프 규칙은 어디서 어떻게 정의될까?
You don't Know JS라는 책에 의하면 자바스크립트는 일반적으로 인터프리터 언어로 분류하지만 사실은 컴파일러 언어라고 한다.
(참고로 인터프리터는 프로그램이 동적으로 실행하면서 코드를 해석하고 컴파일은 프로그래밍이 실행하기 전에 코드를 미리 해석하는 것을 말한다.)
컴파일러는 소스 코드가 실행되기 전에 3단계로 나뉘는데 토크나이징 / 파싱 / 코드 생성으로 이루어진다.
-
토크나이징
문자열을 나누어 토큰이라고 불리는 작은 단위로 만드는 과정이다.
예를들어
var a = 2;
라는 코드를 토크나이징 하게 되면 var / = / 2 / ; 와 같이 작은 문자열 단위로 쪼개고 하나하나를 토큰이라고 정의한다. -
파싱
토큰 배열을 프로그램의 문법 구조를 반영하여 트리 형태로 바꾸는 과정이다.
프로그램이
var a = 2;
를 하나의 트리로 바라보는 것 -
코드 생성
트리를 컴퓨터에서 실행 코드로 바꾸는 과정이다.
var a = 2;
라는 트리를 기계어 집합으로 바꾸어 실제로 a라는 변수를 생성하여 값을 저장
실제로 자바스크립트의 컴파일레이션은 위 3가지 과정 이외에 더 복잡한 과정을 거치며 다른 언어와 다르게 컴파일레이션이 미리 수행되지 않고 코드가 실행되기 겨우 수백만분의 일초 전에 수행한다.
요약하자면 어떤 자바스크립트 코드라도 실행되려면 먼저 컴파일되어야 한다는 것이다. 때문에 자바스크립트는 코드가 실행 되기 바로 직전에 컴파일 되는 인터프리터(동적)언어라고 불리운다.
var a = 2;
코드를 자바스크립트가 동작하기 위해 3가지가 역할을 수행한다.
-
엔진
컴파일레이션의 시작부터 끝까지 모든 과정과 프로그램의 실행을 책임지는 대장이다.
-
컴파일러
엔진의 친구로 파싱과 코드 생성의 모든 잡일을 도맡아 한다.
-
스코프
엔진의 또 다른 친구로 식별자 목록을 작성하고 유지한다.
사람이 var a = 2;
코드를 바라볼 때 하나의 구문으로 바라보지만 자바스크립트 엔진은 2가지 로 나누어 바라본다.
하나는 컴파일러가 처리할 구문이고 다른 하나는 실행과정에서 엔진이 처리할 구문이다.
-
var a = 2;
에서 컴파일러가var a
를 만나면 스코프에 변수 a가 존재하는지? 확인하고 이미 존재한다면 이 선언을 무시하고 지나칠 것이다. 그렇지 않고 스코프 내에 변수 a가 없다면 새로운 변수 a를 스코프 내에 선언할 것이다.(이 단계는 코드 실행 전에 수행된다.) -
이후 컴파일러는 a = 2 대입문을 처리하기 위해 나중에 엔진이 실행할 수 있는 코드를 생성한다. 엔진은 실행 코드가 스코프 내에 a라는 변수를 접근할 수 있는지 확인하며 존재한다면 변수 a를 사용하고 아니라면 다른 곳(중첩 스코프)부분을 살핀다.
-
엔진이 중첩 스코프 안에서도 찾지 못하고 결국 변수 a를 찾지 못하게 된다면 에러가 발생할 것이다.
요약 -> 컴파일러는 스코프 내에 변수가 없다면 선언할 것이며 엔진은 스코프 내에서 변수를 찾으며 변수가 있다면 실행할 것이다.
스코프는 변수를 찾기 위한 규칙의 집합이다. 하지만 실제 프로그램을 만들다보면 대개 고려해야 할 스코프는 여러 개이다.
보통 함수는 함수 안에서 선언될 수 있듯이 스코프도 다른 스코프 안에 중첩될 수 있다.
따라서 식별자를 현재 스코프 내에서 발견하지 못하면 다음 바깥의 스코프로 넘어가 식별자를 찾게 되며 최종적으로 글로벌 스코프라고 불리는 가장 바깥의 스코프에 도달할 때까지 계속한다.
EX)
function foo(a) {
console.log(a + b);
}
var b = 2;
foo(2); // 4
엔진과 스코프는 위와 같은 코드를 보고 다음과 같은 과정을 거친다.
(참고: RHS검색 = 값을 가져와 넘겨주는 것 / LHS검색 = 대입할 대상을 찾는 것)
-
엔진은 현재 스코프 내에서 변수를 찾기 시작하고, 찾지 못하면 한 단계씩 밖에 있는 스코프로 올라온다.
-
최상위 글로벌 스코프에 도달하면 변수를 찾았든 못 찾았든 검색을 멈춘다.
LHS와 RHS를 구분하는 것은 중요하다. 이 두 종류의 검색 방식은 검색한 모든 스코프에서 찾지 못했을 때 서로 다르게 동작하기 때문이다.
RHS는 중첩 스코프 안 어디에서도 변수를 찾지못하면 ReferenceError 를 발생시킨다.
(RHS 검색 결과 변수를 찾았지만 그 값을 가지고 불가능한 일을 시도할 경우(null이나 undefined값을 참조할 때) 엔진은 TypeError를 발생시킨다)
반면에 LHS 검색을 수행하여 변수를 찾지 못하고 프로그램이 Strict Mode
로 동작하고 있는 것이 아니라면 엔진이 자동(암시)적으로 글로벌 스코프에 새로운 변수를 만들어 주게 된다.
출처: You don't know JS