tokenizing
token은 JS에서 의미를 가지는 최소 단위를 의미한다. 구문을 token으로 자르는 행위를 tokenizing이라고 한다.
lexing
token들을 합쳐서 조금 더 의미 있는 lex라는 단위로 만드는 과정이다. 예를 들어 H, H, O가 있을 때 H,H,O 각각은 원자(token)이고 H~2~O는 분자(lex)이다.
parsing
lex들을 프로그램의 문법 구조를 반영하여 중첩 원소를 갖는 트리 형태로 바꾸는 과정을 말한다. 쉽게 말해 lexing을 통해 생성된 lex들을 관계 맺는 과정이다. ⇒ Abstract Syntax Tree(추상 구문 트리)를 생성한다
<h1>
<div>
FEkachu
</div>
</h1>
// 1. tokenizing
// <, div, >, FEkachu, </, div, >
// 2. lexing
// lex {
// type : div
// text : h1
// },
// lex {
// type : h1
// }
// 3. parsing
// lex {
// type : h1
// children : lex {
// type : div
// text : FEkachu
// }
// }
{
const a = 1;
console.log(a); // 1
}
console.log(a); // ReferenceError
var a = 1;
if(true){
var b = 2;
}
console.log(b); // 2
실행 컨텍스트란?
실행 컨텍스트는 4가지 환경에서 생성된다.
실행(전역 코드)
블록 생성 시(ES6 ~ , 블록코드)
함수 실행(함수 내부 코드)
eval()
EC는 함수 실행할 때 참조를 위해 생성되며, 실행 때의 전반적인 상황을 파악하기 위해 생성되는 자료구조이다. EC는 실행 시, scope가 참조하는 대상이 된다.
실행 컨텍스트는 각각 따로 생성되며, global ec를 제외한 ec들은 부모 ec를 담고 있다.
var i = 0; // 1
function runA(){ // 2
console.log(i); // 4
}
runA();// 3
// global EC
// variables : {i (1 실행 시 담김), runA : 함수 내용 (2 실행 시 담김)}
// runA EC (3 실행 시 생성)
// parents : global EC
// variables : {}
// log EC (4 실행 시 생성)
// parents : runA EC
// variables : {data : i}
// 해당 블록에서 설명하는 EC는 간단한 버전
// EC
// lexical environment : 값이 변경되는 것이 기록된다.
// variable environment : snapshot(원본이 담겨있다. ,[environmentRecord, outer-environmentRecord])
// 해당 EC의 this
위의 예제 코드에서 console.log()가 실행되면 log EC가 생성되고, i를 검색하는데 해당 EC에서 찾을 수 없을 시, 부모 EC로 이동하면서 i를 탐색한다. 이를 스코프 체이닝이라고 한다.
변수 선언과 메모리 할당까지 일어난다.
변수 재선언, 재할당도 자유롭게 가능하다.
함수, 블록을 스코프로 인식하지 않는다.
console.log(a); // undefined
var a = 0;
var b = 1;
console.log(b); // 1
변수 선언과 메모리 할당까지 일어나지만, 대입하는 과정이 일어나지 않아 undefined가 출력 되고 아래 코드는 정상적으로 출력된다.
var a = 1;
var a = 3;
console.log(a); // 3
function checkA(){
console.log(a); // undefined
var a = 2;
console.log(a); // 2
}
checkA();
자유롭게 재할당이 가능한 것 확인할 수 있다.
undefined,2를 출력한 console.log()의 모두 해당 scope안에서 a의 해당하는 값을 찾았으므로 스코프 체이닝이 일어나지 않는다.
변수 선언만 일어난다.
변수 재할당은 가능하지만, 재선언은 불가능하다.
함수, 블록을 스코프로 인식한다.
console.log(a);
let a = 0; // ReferenceError : cannot access before initialization
변수 선언만 일어나기 때문에 메모리가 확보되지 않아 ReferenceError가 발생한다.
let a = 1;
let a = 2;
console.log(a); // SyntaxError : Identifier has already declared
---
let a = 1;
a = 2;
console.log(a); // 2
재선언이 불가능한 것을 볼 수 있다.
변수 선언만 일어난다.
변수 재할당, 재선언 모두 불가능하다.
함수, 블록을 스코프로 인식한다.
console.log(a);
const a = 0; // ReferenceError : cannot access before initialization
변수 선언만 일어나기 때문에 메모리가 확보되지 않아 ReferenceError가 발생한다.
const a = 1;
const a = 2;
console.log(a); // SyntaxError : Identifier has already declared
const a = 1;
a = 2;
console.log(a); // TypeError : Assignment to constant variable.
재선언, 재할당 모두 불가능한 것을 확인할 수 있다.
hello(); // hello world!
function hello(){
console.log('hello world!');
}
hello(); // hello world!
// 위치와 관계 없이 잘 실행된다.
---
hello(); // TypeError
var hello = () =>{
console.log('hello world!');
}
hello(); // hello world!
// 함수 표현식과 같은 경우, var, let, const의 변수 생성 규칙을 따른다.
// 해당 식의 경우 var hello -> var은 선언과 할당만 일어나기 때문에 error가 발생
function tdz(){
// TDZ start
console.log(tdzTest); // ReferenceError: Cannot access before initialization
// TDZ finish
let tdzTest = 0;
console.log(tdzTest); // 0
}
tdz();