> For the complete documentation index, see [llms.txt](https://jaemedevs-organization.gitbook.io/frontend_study/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://jaemedevs-organization.gitbook.io/frontend_study/book/core-javascript/ch2.md).

# Chapter 2: 실행 컨텍스트

* 실행 컨텍스트(execution context)는 **실행할 코드에 제공할 환경 정보들을 모아놓은 객체**로, 자바스크립트의 동적 언어로서의 성격을 가장 잘 파악할 수 있는 개념이다.
* 자바스크립트는 어떤 실행 컨텍스트가 활성화되는 시점에 선언된 변수를 위로 끌어올리고(호이스팅), 외부 환경 정보를 구성하고, this 값을 설정하는 등의 동작을 수행하는데, 이로 인해 다른 언어에서는 발견할 수 없는 특이한 현상들이 발생한다.

## 🍄 1. 실행 컨텍스트란?

* 실행할 코드에 제공할 환경 정보들을 모아 놓은 객체로 동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고, 이를 콜 스택에 쌓아올렸다가, 가장 위에 쌓여있는 컨텍스트와 관련 있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장한다.
* 하나의 실행 컨텍스트를 구성할 수 있는 방법으로 전역공간, eval() 함수, 함수 등이 있다. 자동으로 생성되는 전역공간과 악마로 취급받는 eval을 제외하면 우리가 흔히 실행 컨텍스트를 구성하는 방법은 **함수를 실행하는 것** 뿐이다.
* ES6에서는 블록({})에 의해서도 새로운 실행 컨텍스트가 생성된다.

```jsx
// ------ (1)
var a = 1;

function outer() {
	function inner() {
		console.log(a); // undefined
		var a = 3;
	}
	inner(); // ------ (2)
	console.log(a); // 1
}

outer(); // ------ (3)
console.log(a); // 1 
```

* 자바스크립트 코드가 실행되는 순간 (1) 전역 컨텍스트가 콜 스택에 담긴다. 전역 컨텍스트라는 개념은 일반적인 실행 컨텍스트와 특별히 다를게 없다. 최상단의 공간은 코드 내부에서 별도의 실행 명령이 없어도 브라우저에서 자동으로 실행하므로 자바스크립트 파일이 열리는 순간 전역 컨텍스트가 활성화된다고 이해하면 된다.
* 전역 컨텍스트와 관련된 코드들을 순차로 진행하다가 (3)에서 outer 함수를 호출하면 자바스크립트 엔진은 outer에 대한 환경 정보를 수집해서 outer 실행 컨텍스트를 생성한 후 콜 스택에 담는다. 콜스택의 맨 위에 outer 실행 컨텍스트가 놓인 상태가 됐으므로 전역 컨텍스트와 관련된 코드의 실행을 일시중단하고 outer 실행 컨텍스트와 관련된 코드, 즉 outer 함수 내부의 코드들을 순차로 실행한다.
* 다시 (2)의 inner 함수의 실행 컨텍스트가 콜 스택의 가장 위에 담기면 outer 컨텍스트와 관련된 코드의 실행을 중단하고 inner 함수 내부의 코드를 순서대로 진행한다.
* inner 함수 실행이 종료되면 inner 실행 컨텍스트가 콜 스택에서 제거된다. 아래 있던 outer 컨텍스트가 콜스택 맨 위에 존재하므로 이어서 실행하고 출력을 끝으로 outer 함수의 실행이 종료되어 outer 실행 컨텍스트가 콜 스택에서 제거되고, 콜 스택에는 전역 컨텍스트만 남아 있게 된다.
* 마지막으로 실행을 중단했던 (3)에서 이어서 실행한다. 출력을 끝으로 전역 공간에는 더는 실행할 코드가 남아 있지 않아 전역 컨텍스트도 제거되고, 콜 스택에는 아무것도 남지 않는 상태로 종료된다.
* 실행 컨텍스트가 활성화될 때 자바스크립트 엔진은 해당 컨텍스트에 관련된 코드들을 실행하는 데 필요한 환경 정보들을 수집해서 실행 컨텍스트 객체에 저장한다. 이 객체에는 자바스크립트 엔진이 활용할 목적으로 생성할 뿐 개발자가 코드를 통해 확인 불가하다. 여기에는 이런 정보들이 담긴다.
  * VariableEnvironment : 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보. 선언 시점의 LexicalEnvironment의 스냅샷으로 변경사항은 반영되지 않음.
  * LexicalEnvironment : 처음에는 VariableEnvironment와 같지만 변경 사항이 실시간으로 반영된다.
  * ThisBinding : this 식별자가 바라봐야 할 대상 객체

## 🍄 2. VariableEnvironment

* **VariableEnvironment**에 담기는 내용은 **LexicalEnvironment**와 같지만 최초 실행 시의 스냅샷을 유지한다는 점이 다르다.
* 실행 컨텍스트를 생성할 때 **VariableEnvironment**에 정보를 먼저 담은 다음, 이를 그대로 복사해서 **LexicalEnvironment**를 만들고, 이후에 **LexicalEnvironment**를 주로 활용하게 된다.
* **VariableEnvironment**와 **LexicalEnvironment**의 내부는 **environmentRecord**와 **outerEnvironmentReference**로 구성돼 있다.

## 🍄 3. LexicalEnvironment

* **LexicalEnvironment**에 대한 한국어 번역은 문서마다 다른데 ‘어휘적 환경’이라는 단어가 가장 많이 등장한다. ‘어휘적’은 lexical을 영어사전에 대입해서 치환한 것으로 의미가 와 닿지 않고, ‘정적’이라는 말은 수시로 변하는 환경 정보를 의미하는 lexical environment에 대한 적절한 번역으로 볼 수 없다.
* 이보다 ‘사전적인’이 더욱 어울리는 표현이다. 예를 들어 현재 컨텍스트의 내부에는 a, b, c와 같은 식별자들이 잇고 그 외부 정보는 D를 참조하도록 구성돼있다 라는 컨텍스트를 구성하는 환경 정보들을 사전에서 접하는 느낌으로 모아둔 것이다.

### 🌻 3-1. environmentRecord와 호이스팅

* environmentRecord에는 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다.
* 컨텍스트를 구성하는 함수에 지정된 매개변수 식별자, 선언한 함수가 있을 경우 그 함수 자체, var로 선언된 변수의 식별자 등이 식별자에 해당한다.
* 컨텍스트 내부 전체를 처음부터 끝까지 쭉 훑어나가며 순서대로 수집한다.
* 전역 실행 컨텍스트는 변수 객체를 생성하는 대신 자바스크립트 구동 환경이 별도로 제공하는 객체, 즉 전역 객체를 활용한다. 전역 객체에는 브라우저의 `window` , Node.js의 `global` 객체 등이 있다. 이들은 자바스크립트 내장 객체가 아닌 호스트 객체로 분류된다.
* 코드가 실행되기 전임에도 불구하고 자바스크립트 엔진은 이미 해당 환경에 속한 코드의 변수명들을 모두 알고 있게 된다. 그렇다면 엔진의 실제 동작 방식 대신에 ‘자바스크립트 엔진은 식별자들을 최상단으로 끌어올려놓은 다음 실제 코드를 실행한다’라고 생각하더라도 코드를 해석하는데 문제 될 것 이 전혀 없을 것이다. 여기서 `호이스팅` 이라는 개념이 등장한다.
* environmentRecord에는 매개변수의 이름, 함수 선언, 변수명 등이 담긴다고 했다.

```jsx
function a(x){ // 수집 대상 1(매개변수)
	console.log(x); // (1)
	var x; // 수집 대상 2(변수 선언)
	console.log(x); // (2)
	var x = 2; // 수집 대상 3(변수 선언)
	console.log(x); // (3)
}

a(1)
```

* 우선 호이스팅이 되지 않았을 때의 값을 예상해보자. (1)에는 함수 호출 시 전달한 1이 출력되고, (2)에는 선언된 변수 x에 할당한 값이 없으므로 undefined가 출력되고, (3)에서는 2가 출력될 것 같다. 실제로는 어떤 값이 나올까?

```jsx
function a(){
	var x = 1; // 수집 대상 1(매개변수 선언)
	console.log(x); // (1)
	var x; // 수집 대상 2(변수 선언)
	console.log(x); // (2)
	var x = 2; // 수집 대상 3(변수 선언)
	console.log(x); // (3)
}

a()
```

* environmentRecord는 현재 실행될 컨텍스트의 대상 코드 내에 어떤 식별자들이 있는지에만 관심이 있고, 각 식별자에 어떤 값이 할당될 것인지는 관심이 없다.
* 변수를 호이스팅할 때 변수명만 끌어올리고 할당 과정은 원래 자리에 그대로 남겨둔다. 매개변수의 경우도 마찬가지이다. environmentRecord의 관심사에 맞춰 수집대상 1,2,3을 순서대로 끌어올리고 나면 다음과 같은 형태로 바뀐다.

```jsx
function a(){
	var x; // 수집 대상 1의 변수 선언 부분
	var x; // 수집 대상 2의 변수 선언 부분
  var x; // 수집 대상 3의 변수 선언 부분

	x = 1; // 수집 대상 1의 할당 부분
	console.log(x); // (1)
	console.log(x); // (2)
	x = 2; // 수집 대상 3의 할당 부분
	console.log(x); // (3)
}

a(1);
```

* 2번째 줄 : 변수 x를 선언한다. 이때 메모리에서는 저장할 공간을 미리 확보하고, 확보한 공간의 주솟값을 변수 x에 연결해둔다.
* 3,4번째 줄 : 다시 변수 x를 선언한다. 이미 선언된 변수 x가 있으므로 무시한다.
* 6번째 줄 : x에 1를 할당하라고 한다. 우선 숫자 1을 별도의 메모리에 담고, x와 연결된 메모리 공간에 숫자 1을 가리키는 주솟값을 입력한다.
* 7,8번째 줄 : 1이 출력된다.
* 9번째 줄 : x에 2를 할당하라고 한다. 숫자 2를 별도의 메모리에 담고, 그 주솟값을 든 채로 x와 연결된 메모리 공간으로 간다. 여기에 숫자 1을 가리키는 주솟값이 들어있는데 이걸 2의 주솟값으로 대치한다.
* 10번째 줄 : 2가 출력되고 실행 컨텍스트가 콜 스택에서 제거된다.

```jsx
function a(){
	console.log(b); // (1)
	var b = 'bbb'; // 수집 대상 1(변수 선언)
	console.log(b); // (2)
	function b() {
		console.log(b); // (3)
	}
}

a();
```

* 마찬가지로 출력결과를 예상해보면 (1)은 undefined가 나올 것 같고, (2)는 bbb, (3)은 함수가 출력될 것 같다. 실제로도 그럴까?
* a함수를 실행하는 순간 a함수의 실행 컨텍스트가 생성된다. 이때 변수명과 함수 선언의 정보를 위로 끌어올린다. 변수는 선언부와 할당부를 나누어 선언부만 끌어올리는 반면 함수 선언은 함수 전체를 끌어올린다.

```jsx
function a(){
	var b; // 수집 대상 1. 변수는 선언부만 끌어올린다.
	function b() {} // 수집 대상 2. 함수 선언은 전체를 끌어올린다.

	console.log(b); // (1)
	b = 'bbb'; // 변수의 할당부는 원래 자리에 남겨둔다.
	console.log(b); // (2)
	console.log(b); // (3)
}
```

* 해석의 편의를 위해 한가지만 더 바꿔보자. 호이스팅이 끝난 상태에서의 함수 선언문은 함수명으로 선언한 변수에 함수를 할당한 것처럼 여길 수 있다.

```jsx
function a(){
	var b;
	var b = function b() {}

	console.log(b); // (1)
	b = 'bbb';
	console.log(b); // (2)
	console.log(b); // (3)
}
```

* 2번째 줄 : 변수 b를 선언한다. 메모리에서 저장할 공간을 미리 확보하고, 확보한 공간의 주솟값을 b에 연결한다.
* 3번째 줄 : 다시 변수 b를 선언하고 함수 b를 선언된 변수 b에 할당하라고 한다. 이미 선언된 변수 b가 있으므로 선언과정은 무시한다. 함수는 별도의 메모리에 담길 것이고, 함수가 저장된 주솟값을 b와 연결된 공간에 저장한다. 변수 b는 함수를 가리키게 된다.
* 5번째 줄 : 변수 b에 할당된 함수 b를 출력한다.
* 6번째 줄 : 변수 b에 ‘bbb’를 할당하라고 한다. b에 연결된 메모리 공간에 문자열 ‘bbb’가 담긴 주솟값으로 덮어쓴다. 변수 b는 문자열 ‘bbb’를 가리키게 된다.
* 7,8번째 줄 : (2), (3)모두 bbb가 출력되고, 함수 내부의 모든 코드가 실행됐으므로 실행 컨텍스트가 콜 스택에서 제거된다.
* 호이스팅을 다루는 김에 함께 알아두면 좋은 내용은 **함수 선언문**과 **함수 표현식**이 있다.
* 둘 모두 함수를 새롭게 정의할 때 쓰이는 방식인데, 그중 함수 선언문은 function 정의부만 존재하고 별도의 할당 명령이 없은 것을 의미하고, 반대로 함수 표현식은 정의한 function을 별도의 변수에 할당하는 것을 말한다.
* 함수 선언문의 경우 반드시 함수명이 정의돼 있어야 하는 반면, 함수 표현식은 없어도 된다.
* 함수명을 정의한 함수 표현식을 기명 함수 표현식, 정의하지 않은 것을 익명 함수 표현식이라고 부리기도 하는데, 일반적으로 함수 표현식은 익명 함수 표현식을 말한다.

```jsx
function a() { /*...*/ } // 함수 선언문. 함수명 a가 곧 변수명
a();

var b = function() { /*...*/ } // (익명) 함수 표현식. 변수명 b가 곧 함수명.
b();

var c = function d() { /*...*/ } // 기명 함수 표현식. 변수명은 c, 함수명은 d.
c();
d(); // Error!
```

```jsx
console.log(sum(1, 2));
console.log(multiply(3,4));

function sum(a,b) { // 함수 선언문 sum
	return a+b; 
}

var multiply = function (a,b) { // 함수 표현식 multiply
	return a*b;
}
```

* 실행 컨텍스트의 lexicalEnvironment는 두 가지 정보를 수집하는데, 여기서는 그 중에서 environmentRecord의 정보 수집 과정에서 발생하는 호이스팅을 살펴보는 중이다.

```jsx
var sum = function sum(a, b){ // 함수 선언문은 전체를 호이스팅한다.
	return a + b;
}

var multiply; // 변수는 선언부만 끌어올린다.

console.log(sum(1,2)); // 3
console.log(multiply(3,4)); // error : multiply is not a function

multiply = function(a,b) { // 변수의 할당부는 원래 자리에 남겨둔다.
	return a * b;
} 
```

* 함수 선언문은 전체를 호이스팅한 반면 함수 표현식은 변수 선언부만 호이스팅했다. 함수도 하나의 값으로 취급할 수 있다는 것이 바로 이런 것이다. 함수를 다른 변수에 값으로써 할당한 것이 곧 함수 표현식이다.
* 함수 선언문은 초급자들이 자바스크립트를 좀 더 쉽게 접근할 수 있게 해주는 측면도 있지만 반대로 큰 혼란을 일으키는 원인이 되기도 한다. 글을 위에서 아래로 읽는 문화환경에서는 받아들이기 어려울 수도 있고 프로그래밍 언어도 인간이 만든 것이니만큼 선언한 후에야 호출할 수 있다라는 편이 훨씬 자연스러울 것이다.
* 함수선언문이 혼란스러운 개념이라하는 이유는 실무에서도 존재한다.

```jsx
console.log(sum(3,4));

function sum(x,y) {
	return x + y;
}

var a = sum(1,2);

function sum(x,y) {
	return x + '+' + y + '=' + (x+y);
}

var c = sum(1, 2);
console.log(c);
```

* 전역 컨텍스트가 활성화될 때 전역 공간에 선언된 함수들이 모두 가장 위로 끌어올려진다. 동일한 변수명에 서로 다른 값을 할당할 경우 나중에 할당한 값이 먼저 할당한 값을 덮어 씌운다. 따라서 코드를 실행하는 중에 실제로 호출되는 함수는 오직 마지막에 할당한 함수이다.
* 만약 sum 함수를 표현식으로 정의했다면 첫번째 줄에서는 에러가 나긴 하겠지만 그 이후부턴 제대로 동작할 것이다. 그리고 에러 검출도 확인되므로 더욱 빠른 타이밍에 손쉽게 디버깅할 수 있었을 것이다.

```jsx
console.log(sum(3,4));

var sum = function(x,y) {
	return x + y;
}

var a = sum(1,2);

var sum = function(x,y) {
	return x + '+' + y + '=' + (x+y);
}

var c = sum(1, 2);
console.log(c);
```

### 🌻 3-2. 스코프, 스코프 체인, outerEnvironmentReference

* \*\*스코프(scope)\*\*란 식별자에 대한 유효범위이다. 어떤 경계 A의 외부에서 선언한 변수는 A의 외부뿐 아니라 A의 내부에서도 접근이 가능하지만, A내부에서 선언한 변수는 오직 A의 내부에서만 접근할 수 있는것.
* 스코프의 개념은 대부분의 언어에 존재한다. 자바스크립트도 예외는 아닌데, 다만 ES5까지의 자바스크립트는 특이하게도 전역 공간을 제외하면 오직 함수에 의해서만 스코프가 생성됬었다.
* 이러한 식별자의 유효범위를 안에서부터 바깥으로 차례로 검색해나가는 것을 **스코프 체인**이라고 한다. 그리고 이를 가능케 하는 것이 바로 LexicalEnvironment의 두번째 수집자료인 outerEnvironmentReference이다.
* outerEnvironmentReference는 현재 호출된 함수가 선언될 당시의 LexicalEnvironment를 참조한다.
* 예를 들어 A 함수 내부에 B함수를 선언하고 다시 B함수 내부에 C함수를 선언한 경우, 함수 C의 outerEnvironmentReference는 다시 함수 B의 LexicalEnvironment를 참조한다. 함수 B의 LexicalEnvironment에 있는 outerEnvironmentReference는 다시 함수 B가 선언되던 때 A의 LexicalEnvironment를 참조할 것이다. 이처럼 outerEnvironmentReference는 연결리스트의 형태를 띤다.
* 선언 시점의 LexicalEnvironment를 계속 찾아 올라가면 마지막엔 전역 컨텍스트의 LexicalEnvironment가 있을 것이다. 또한 각 outerEnvironmentReference는 오직 자신이 선언된 시점의 LexicalEnvironment만 참조하고 있으므로 가장 가까운 요소부터 차례대로 접근할 수 있고 다른 순서로 접근하는 것은 불가능 할 것이다.
* 이런 구조적 특성 덕분에 여러 스코프에서 동일한 식별자를 선언한 경우에는 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근 가능하다.

```jsx
var a = 1;
var outer = function(){
	var inner = function(){
		console.log(a);
		var a = 3;
	}
	inner();
	console.log(a);
}
outer();
console.log(a);
```

* 시작 : 전역 컨텍스트가 활성화 된다. 전역 컨텍스트의 environmentRecord에 {a, outer} 식별자를 저장한다. 전역 컨텍스트는 선언 시점이 없으므로 전역 컨텍스트의 outerEnvironment-Reference에는 아무것도 담기지 않는다.
* 1, 2 줄 : 전역 스코프에 있는 a에 1을 outer에 함수를 할당한다.
* 10 줄 : outer 함수를 호출 한다. 이에 전역 컨텍스트의 코드는 10번째 줄에서 임시중단되고 outer 실행 컨텍스트가 활성화되어 2번째 줄로 이동한다.
* 2 줄 : outer 실행 컨텍스트의 environmentRecord에 {inner} 식별자를 저장한다. outerEnvironmentReference에는 outer 함수가 선언될 당시의 LexicalEnvironment가 담긴다. outer 함수는 전역 공간에서 선언됐으므로 전역 컨텍스트의 LexicalEnvironement를 참조복사한다. 이를 \[GLOBAL, {a, outer}] 라고 표시한다. 첫째는 실행 컨텍스트의 이름 두번째는 environmentRecord의 객체이다.
* 3 줄 : outer 스코프에 있는 변수 inner에 함수를 할당한다.
* 7 줄 : inner 함수를 호출한다. 이에 따라 outer 실행 컨텍스트의 코드는 7번째 줄에서 임시중단되고, inner 실행 컨텍스트가 활성화되어 3번째 줄로 이동한다.
* 3 줄 : inner 실행 컨텍스트의 environmentRecord에 {a} 식별자를 저장한다. outerEnvironmentReference에는 inner 함수가 선언될 당시의 LexicalEnvironment가 담긴다. inner 함수는 outer 함수 내부에서 선언됐으므로 outer 함수의 LexicalEnvironment, 즉 \[outer, {inner}]를 참조복사 한다.
* 4 줄 : 식별자 a에 접근하고자 한다. 현재 활성화 상태인 inner 컨텍스트의 environmentRecord에서 a를 검색한다. a가 발견됬지만 여기에는 아직 할당된 값이 없기에 undefined를 출력한다.
* 5 줄 : inner 스코프에 있는 변수 a에 3을 할당한다.
* 6 줄 : inner 함수 실행이 종료된다. inner 실행 컨텍스트가 콜 스택에서 제거되고 바로 아래의 outer 실행 컨텍스트가 다시 활성화되면서 앞서 중단했던 7번째 줄의 다음으로 이동한다.
* 7 줄 : 식별자 a의 접근하려고 하면 자바스크립트 엔진은 활성화된 실행 컨텍스트의 LexicalEnvironment에 접근한다. 첫 요소인 environmentRecord에서 a가 있는지 찾아보고 없으면 outerEnvironmentReference에 있는 environmentRecord로 넘어가는 식으로 계속해서 검색한다. ㅇ즉 전역 LexicalEnvironment에 a가 있으니 그 a에 저장된 값 1을 반환한다.
* 9 줄 : outer 함수 실행이 종료된다. outer 실행 컨텍스트가 콜 스택에서 제거되고, 바로 아래의 전역 컨텍스트가 다시 활성화된다.
* 11 줄 : 식별자 a에 접근하고자 한다. 현재 활성화 상태인 전역 컨텍스트의 environmentRecord에서 a를 검색하고 출력한다.(1)
* 스코프 체인 상에 있는 변수라고 해서 무조건 접근 가능한 것은 아니다. 위 코드에서 식별자 a는 전역공간, inner 함수 내부에서도 선언했다. inner 함수 내부에서 a에 접근하려고 하면 무조건 스코프 체인 상의 첫 번째 인자 즉 inner 스코프의 LexicalEnvironment부터 검색할 수 밖에 없다. inner 스코프의 LexicalEnvironment에 a가 존재하므로 스코프 체인 검색을 더이상 진행하지 않고 반환한다. 즉 inner 함수 내부에서 a 변수를 선언했기 때문에 전역 공간에서 선언한 동일한 이름의 a변수에는 접근할 수 없는 셈이다. 이를 `변수 은닉화` 라고 한다.

## 🍄 4. this

* 실행 컨텍스트의 thisBinding에는 this로 지정된 객체가 저장된다. 실행 컨텍스트 활성화 당시 this가 지정되지 않은 경우 this에는 전역 객체가 저장된다. 그 밖에는 함수를 호출하는 방법에 따라 this에 저장되는 대상이 다르다.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jaemedevs-organization.gitbook.io/frontend_study/book/core-javascript/ch2.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
