본문은 JavaScript 언어에 대한 이해를 높이기 위해 "You Don't Know JS(카일 심슨 저)"를 읽고 공부한 내용을 정리한 글입니다. 공부가 목적이기 때문에 필요에 따라 생략이나 수정, 추가된 부분이 있을 수 있습니다.
1. 스코프란?
스코프는 참조 대상 식별자(identifier, 변수/함수의 이름과 같이 어떤 대상을 다른 대상과 구분하여 식별할 수 있는 유일한 이름)를 찾아내기 위한 규칙이다. 자바스크립트는 이 규칙대로 식별자를 찾는다.
2. 스코프의 구분
자바스크립트의 스코프는 다음과 같이 2가지로 나뉜다.
1. 전역 스코프 (Global Scope)
프로그램 내 어디에서나 참조할 수 있다.
2. 지역 스코프 (Local Scope or Function-Level Scope)
함수 코드 블록이 만든 스코프로 함수 자신과 하위 함수에서만 참조할 수 있다.
이러한 스코프에 따라 변수를 다음 두 가지로 나눠볼 수 있다.
1. 전역 변수 (Global Variable)
전역에서 선언된 변수이며 어디에서든 참조할 수 있다.
2. 지역 변수 (Local Variable)
지역(함수) 내에서 선언된 변수이며. 그 지역과 그 지역의 하부 지역에서만 참조할 수 있다.
3. 자바스크립트 스코프의 특징
3-1) 렉시컬 스코프
다음의 코드를 예측해보자.
var x = 1;
function foo() {
var x = 10;
bar();
}
function bar() {
console.log(x);
}
foo(); // ?
bar(); // ?
결과는 다음과 같다.
1
1
위 예제의 실행 결과는 함수 bar의 상위 스코프가 무엇인지에 따라 결정된다. 두가지 패턴을 예측할 수 있다.
- 첫 번째는 함수를 어디서 호출하였는지에 따라 상위 스코프를 결정하는 것이고, -> 동적 스코프(Dynamic Scope)
- 두 번째는 함수를 어디서 선언하였는지에 따라 상위 스코프를 결정하는 것이다. -> 렉시컬 스코프(Lexical Scope) or 정적(Static Scope)
첫 번째 방식으로 함수의 상위 스코프를 결정한다면 함수 bar의 상위 스코프는 함수 foo와 전역일 것이고, 두번째 방식으로 함수의 스코프를 결정한다면 함수 bar의 스코프는 전역일 것이다. 자바스크립트를 비롯한 대다수의 프로그래밍 언어는 두 번째 방식인 렉시컬 스코프를 따른다.
렉시컬 스코프(Lexical Scope) 함수를 어디서 호출하는지가 아니라 함수를 어디에 선언하는지에 따라 정의되는 스코프를 의미한다. '렉시컬'이라는 단어가 쓰인 이유는 컴파일레이션의 렉싱(Lexing) 단계에서는 모든 식별자가 어디서 어떻게 선언됐는지 파악하여 실행 단계에서 어떻게 확인자를 검색할지 예상할 수 있도록 도와주는데, 렉시컬 스코프가 이러한 렉싱 타임에 정의되기 때문이다.
컴파일레이션
자바스크립트는 일반적으로 '동적' 또는 '인터프리터' 언어로 분류하나, 사실은 '컴파일러 언어'다. 물론 자바스크립트가 전통적인 컴파일러 언어처럼 코드를 미리 컴파일하거나 컴파일한 결과를 분산 시스템에서 이용할 수 있는 것은 아니지만, 이러한 언어들의 컴파일러가 하는 일의 상당 부분을 수행한다.
전통적인 컴파일러 언어의 처리 과정에선 소스 코드가 실행되기 전에 보통 3단계를 거치는데, 이를 컴파일레이션(Compilation)이라고 한다. 컴파일레이션의 각 단계를 간략하게 설명하자면 다음과 같다.
1. 토크나이징(Tokenizing)/렉싱(Lexing)
문자열을 나누어 토큰이라 불리는 의미 있는 조각으로 만드는 과정이다.
2. 파싱(Parsing)
토큰 배열을 프로그램의 문법 구조를 반영하여 중첩 원소를 갖는 트리 형태로 바꾸는 과정이다. 파싱의 결과로 만들어진 트리를 AST(추상 구문 트리, Abstract Syntax Tree)라 부른다.
3. 코드 생성(Code-Generation)
AST를 컴퓨터에서 실행 코드로 바꾸는 과정이다.
3-2) 중첩 스코프
프로그램을 짤 때 일반적으로 고려해야 할 스코프는 여러 개다.하나의 블록이나 함수는 다른 블록이나 함수 안에 중첩될 수 있으므로 스코프도 다른 스코프 안에 중첩(Nest)될 수 있다. 따라서 대상 변수를 현재 스코프에서 발견하지 못한다면, 엔진은 대상 변수를 찾을 때까지 바로 바깥에 존재하는 다음 스코프로 넘어가고, 글로벌 스코프에 도달할 때까지 이러한 작업을 반복한다.
3-3) 함수 레벨 스코프
C나 자바 등의 프로그래밍 언어는 블록 레벨 스코프(Block-Level Scope)를 따른다. 블록 레벨 스코프란 코드 블록 내에서 유효한 스코프를 의미한다. 여기서 "유효하다"라는 말은 "참조(접근)할 수 있다"라는 의미이다.
int main(void) {
// block-level scope
if (1) {
int x = 5;
printf("x = %d\n", x);
}
printf("x = %d\n", x); // use of undeclared identifier 'x'
return 0;
}
이와 달리 자바스크립트는 기본적으로 함수 레벨 스코프(Functional-Level Scope)를 따른다. 함수 레벨 스코프란 함수 코드 블록 내에서 선언된 변수는 함수 코드 블록 내에서만 유효하고, 함수 외부에서는 유효하지 않다. 하지만, ES6부터 도입된 let, const를 통해 블록 레벨 스코프를 사용할 수 있다. const, let, var에 대한 차이는 여기에서 확인할 수 있다.
var x = 0;
{
var x = 1;
console.log(x); // 1
}
console.log(x); // 1
let y = 0;
{
let y = 1;
console.log(y); // 1
}
console.log(y); // 0
const z = 0;
{
const z = 1;
console.log(z); // 1
}
console.log(z); // 0
4. 참조
'프로그래밍 언어 > JavaScript + TypeScript' 카테고리의 다른 글
[You Don't Know JS] 7. 클로저(Closure) (0) | 2021.07.06 |
---|---|
[You Don't Know JS] 6. 호이스팅(Hoisting) (0) | 2021.07.03 |
[You Don't Know JS] 4. 문법 (0) | 2021.06.29 |
[You Don't Know JS] 3. 네이티브(Native) (0) | 2021.06.24 |
[스크랩] JavaScript(ES6), TypeScript, Node.js 개발자를 위한 무료 e북 4가지 (0) | 2021.06.22 |