자료구조, 알고리즘 공부를 슬슬 해야겠다는 생각이 들어서 트리 구조에 대해 공부하다가 JS로 구현된 코드를 만났다.
class Node {
constructor(data) {
this.data = data;
this.children = [];
}
add(data) {
this.children.push(new Node(data)); 👈 🔥
}
remove(data) {
this.children = this.filter((child) => child.data !== data);
}
}
class Tree {
constructor() {
this.root = null;
}
}
const t = new Tree();
t.root = new Node('a');
t.root.add('b');
t.root.add('c');
t.root.children[0].add('d');
console.log(t);
그런데 문득 👈 🔥 이 부분이 궁금해졌다.
Node
라는Class
의 정의가 다 끝나지 않았는데 어떻게new Node
라고 접근이 가능한 거지..?
클래스라서 가능한 걸까? 궁금해서 아주 간단한 코드를 작성해보았다.
const a = () => {
console.log('a 함수');
console.log(a);
};
a();
// 👇 결과
// a 함수
// [Function: a]
여기서도 a 함수의 정의가 끝나지 않았는데(?) console.log(a)
에서 에러 없이 [Function: a]
라고 출력이 된다. 생각해보면 재귀 함수를 쓸 때도 별 생각 없이 이런 식으로 코드를 작성했었는데, 어떤 원리로 동작하는 것인지 실행 컨텍스트 라는 키워드를 통해 알아보자.
let x = 10;
function timesTen(a) {
return a * 10;
}
let y = timesTen(x);
console.log(y); // 100
위 코드에서는
x
라는 변수에 10을 할당한다.- 인자를 받아 10을 곱하는
timesTen
함수를 선언한다.x
를 파라미터로 전달하여timesTen
함수를 호출하고, 그 리턴값을y
라는 변수에 저장한다.y
를 콘솔에 찍어본다.
자바스크립트 엔진이 스크립트를 실행할 때, 실행 컨텍스트들을 생성한다. 각각의 실행 컨텍스트는 2개의 phase를 갖는다. the creation phase
and the execution phase
맨 처음 스크립트가 실행될 때, 자바스크립트 엔진은 Global Execution Context
를 생성한다. Global Execution Context
의 creation phase
에서는 다음과 같은 일을 한다.
window
, Node.js에서는 global
이다.undefined
로 해서 저장한다.예제 코드의 creation phase 동안에는
x
, y
, 함수 선언 timesTen
을 Global Execution Context에 저장한다. 그리고 변수 x
, y
를 undefined
로 초기화한다.creation phase 다음에는, global execution context는 execution phase로 옮겨간다.
execution phase 동안에는, 자바스크립트 엔진이 코드를 한 줄 한 줄 실행한다. 변수에 값(values)을 할당하고, function calls를 실행한다.
매 함수 호출마다, 자바스크립트 엔진은 새로운 Function Execution Context
를 생성한다. funtion execution context는 global execution context와 비슷한데, global object를 생성하는 대신 arguments
object를 생성한다. arguments
object는 함수에게 전달된 모든 파라미터의 reference를 가지고 있다.
예제 코드에서는, function execution context가 함수에게 전달된 파라미터의 references를 가지고 있는 arguments
object를 생성하고, this
에 global object를 셋팅하고, 파라미터인 a
를 undefined
로 초기화한다.
function execution context의 execution phase 동안에는, 파라미터 a
에 10
을 할당하고 global execution context에게 결과값 100
을 리턴한다.
이러한 작업을 위해 자바스크립트 엔진은 call stack
이라는 자료구조를 사용한다.
그럼 다시 처음에 작성했던 코드를 살펴보자.
const a = () => {
console.log('a 함수');
console.log(a);
};
a();
// 👇 결과
// a 함수
// [Function: a]
최초에 Global Execution Context가 생성되면,
1. the creation phase 에서는
a
)과 변수(없음)를 undefined
로 초기화해서 저장. 즉, 변수 a
가 undefined
인 상태2. the execution phase 에서는
👉 코드를 한 줄씩 실행하면서, 변수에 값 할당하는 코드가 있다면 할당하고, 함수 호출을 만나면 Function Execution Context를 실행한다.
a
에 함수 () => { ... }
를 할당 🍎🍎🍎a();
를 만나면 함수 a의 Function Execution Context 생성3. 함수 a의 Function Execution Context의 the creation phase
4. 함수 a의 Function Execution Context의 the execution phase
a
블럭 안에 있는 코드를 한 줄씩 실행console.log
또한 Function Execution Context가 생성되지만 과정 생략..console.log
가 호출되니 콜스택에 올라가서 실행되어 a 함수 라고 콘솔에 찍힘console.log
또한 호출되어 실행되는데 a
를 콘솔에 찍어야 함a
가 뭐야?? 함수 a
의 Function Execution Context 안에 저장된 variables 중에는 a
라는 게 없음scope chain
을 타고 올라가자!! Global Execution Context 에서 a
를 찾아봄a
에 함수를 할당했었음. 이 정보를 가져온다.[Function: a]
라고 출력이 이후에는 함수 a의 Function Execution Context가 콜스택에서 지워지며 Global Execution Context로 돌아가고, Global Execution Context에도 남아있는 코드가 없으므로 Global Execution Context 또한 콜스택에서 지워지며 스크립트가 종료된다.
차근차근 과정을 생각해보니, 실행 컨텍스트 뿐 아니라 스코프 체인이라는 원리도 적용되어 있다는 것을 알게 되었다. 자바스크립트의 동작에는 정말 모든 개념들이 맞물려있다는 생각이 든다. 다음에는 스코프 체인에 대해서 정리해봐야겠다.
쉽게 이해가 되도록 잘써주셨네요! 👍👍👍