
자바스크립트의 **실행 컨텍스트**는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체자바스크립트는 어떤 실행 컨텍스트가 활성화되는 시점에 다음과 같은 일을 함
이런 현상들 때문에 JS에서는 다른 언어랑은 다른 특징들이 나타남.
실행 컨텍스트를 이해하기 위해서는, 콜 스택에 대한 이해가 반드시 필요. 자, 그 전에 “스택”이라는 개념에 대해서 먼저 이해를 해야함.
(이미지 출처 : https://velog.io/@leejuhwan/스택STACK과-큐QUEUE)콜스택에 쌓아올림. 가장 위에 쌓여있는 컨텍스트와 관련된 코드를 실행하는 방법으로 코드의 환경 및 순서를 보장할 수 있음.구성방법(여러가지가 있지만, 사실 함수만 생각하면 됨 👍)
실행컨텍스트 구성 예시 코드
// ---- 1번
var a = 1;
function outer() {
function inner() {
console.log(a); //undefined
var a = 3;
}
inner(); // ---- 2번
console.log(a);
}
outer(); // ---- 3번
console.log(a);
실행컨텍스트 구성 순서
위 코드는 아래 순서로 진행이 됨
(콜 스택에 쌓이는 실행컨텍스트에 의해 순서가 보장!)
코드실행 → 전역(in) → 전역(중단) + outer(in) → outer(중단) + inner(in) → inner(out) + outer(재개) → outer(out) + 전역(재개) → 전역(out) → 코드종료
결국은 특정 실행 컨텍스트가 생성되는(또는 활성화되는) 시점이 콜 스택의 맨 위에 쌓이는(노출되는) 순간을 의미. 곧, 현재 실행할 코드에 해당 실행 컨텍스트가 관여하게 되는 시점을 의미한다고 받아들여주면 정확! 👍👍
1. VariableEnvironment
1. 현재 컨텍스트 내의 식별자 정보(=record)를 갖고있음.
1. **`var a = 3`**
2. 위의 경우, **`var a`**를 의미
2. 외부 환경 정보(=outer)를 갖고있음.
3. 선언 시점 LexicalEnvironment의 **snapshot**
2. LexicalEnvironment
1. VariableEnvironment와 동일하지만, 변경사항을 실시간으로 반영.
3. ThisBinding
1. this 식별자가 바라봐야할 객체
VE : 스냅샷을 유지.
LE : 스냅샷을 유지하지 않아요. 즉, 실시간으로 변경사항을 계속해서 반영
결국, 실행 컨텍스트를 생성할 때, VE에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LE를 만들고 이후에는 주로 LE를 활용
개요
현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장(수집) 기록된다라고 이해해보면, record라는 말과 일맥상통
수집 대상 정보 : 함수에 지정된 매개변수 식별자, 함수 자체, var로 선언된 변수 식별자 등
컨텍스트 내부를 처음부터 끝까지 순서대로 훑어가며 수집
💡 순서대로 **수집**한다고 했지, 코드가 **실행**된다고 하지는 않음!호이스팅
변수정보 수집을 모두 마쳤더라도 아직 실행 컨텍스트가 관여할 코드는 실행 전의 상태(JS 엔진은 코드 실행 전 이미 모든 변수정보를 알고 있는 것)
변수 정보 수집 과정을 이해하기 쉽게 설명한 ‘가상 개념’
💡 가상개념이라는 말은, 실제로는 그렇진 않더라도 사람이 이해하기 쉬운 말로 풀어 표현했다는 것을 의미 😄호이스팅 규칙
호이스팅 법칙 1 : 매개변수 및 변수는 선언부를 호이스팅
<적용 전>
//action point 1 : 매개변수 다시 쓰기(JS 엔진은 똑같이 이해한다)
//action point 2 : 결과 예상하기
//action point 3 : hoisting 적용해본 후 결과를 다시 예상해보기
function a (x) {
console.log(x);
var x;
console.log(x);
var x = 2;
console.log(x);
}
a(1);
<매개변수 적용>
//action point 1 : 매개변수 다시 쓰기(JS 엔진은 똑같이 이해한다)
//action point 2 : 결과 예상하기
//action point 3 : hoisting 적용해본 후 결과를 다시 예상해보기
function a () {
var x = 1;
console.log(x);
var x;
console.log(x);
var x = 2;
console.log(x);
}
a(1);
<호이스팅 적용>
//action point 1 : 매개변수 다시 쓰기(JS 엔진은 똑같이 이해한다)
//action point 2 : 결과 예상하기
//action point 3 : hoisting 적용해본 후 결과를 다시 예상해보기
function a () {
var x;
var x;
var x;
x = 1;
console.log(x);
console.log(x);
x = 2;
console.log(x);
}
a(1);
자, 예상을
1 → undefined → 2로 예상했지만
실제로는
1, 1, 2 라는 결과가 나옴
호이스팅이라는 개념을 모르면 예측이 불가능한 어려운 결과
호이스팅 법칙 2 : 함수 선언은 전체를 호이스팅
마찬가지로, 2가지 action points에 따라 진행
<적용 전>
```jsx
//action point 1 : 결과 값 예상해보기
//action point 2 : hoisting 적용해본 후 결과를 다시 예상해보기
function a () {
console.log(b);
var b = 'bbb';
console.log(b);
function b() { }
console.log(b);
}
a();
```
<호이스팅 적용>
```jsx
//action point 1 : 결과 값 예상해보기
//action point 2 : hoisting 적용해본 후 결과를 다시 예상해보기
function a () {
var b; // 변수 선언부 호이스팅
function b() { } // 함수 선언은 전체를 호이스팅
console.log(b);
b = 'bbb'; // 변수의 할당부는 원래 자리에
console.log(b);
console.log(b);
}
a();
```
해석을 편하게 하기 위해서 **함수선언문을 함수 표현식으로** 바꿔볼게요!
```jsx
//action point 1 : 결과 값 예상해보기
//action point 2 : hoisting 적용해본 후 결과를 다시 예상해보기
function a () {
var b; // 변수 선언부 호이스팅
**var b = function b() { } // 함수 선언은 전체를 호이스팅**
console.log(b);
b = 'bbb'; // 변수의 할당부는 원래 자리에
console.log(b);
console.log(b);
}
a();
```
이번에도 우리의 예상은 틀렸네요.
> 에러(또는 undefined), ‘bbb’, b함수
>
라고 나올 것 같았지만, 실제로는
> b함수, ‘bbb’, ‘bbb’
>
라는 결과가 나왔어요. 이 또한 호이스팅을 고려하지 않고는 결과를 예측하기가 매우 어려웠어요. 호이스팅을 다루는 김에, **함수의 정의방식 3가지**와 **주의해야 할 내용**을 살짝 짚고 넘어가 보도록 하겠습니다 😉
함수 선언문, 함수 표현식
함수 정의의 3가지 방식
// 함수 선언문. 함수명 a가 곧 변수명
// function 정의부만 존재, 할당 명령이 없는 경우
function a () { /* ... */ }
a(); // 실행 ok
// 함수 표현식. 정의한 function을 별도 변수에 할당하는 경우
// (1) 익명함수표현식 : 변수명 b가 곧 변수명(일반적 case에요)
var b = function () { /* ... */ }
b(); // 실행 ok
// (2) 기명 함수 표현식 : 변수명은 c, 함수명은 d
// d()는 c() 안에서 재귀적으로 호출될 때만 사용 가능하므로 사용성에 대한 의문
var c = function d () { /* ... */ }
c(); // 실행 ok
d(); // 에러!
주의해야 할 내용
함수 선언문, 함수 표현식
다시 잠깐 교통정리
💡 - 실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.
계실질적인 차이를 예시
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;
}
LE는 record와 outer를 수집 그 중, record를 수집하는 과정에서 hoisting이 일어나고, 우리가 익히 알고있는대로 위로 쭉 끌어올려본 결과를 다시 써보면 아래와 같음
// 함수 선언문은 전체를 hoisting
function sum (a, b) { // 함수 선언문 sum
return a + b;
}
// 변수는 선언부만 hoisting
var multiply;
console.log(sum(1, 2));
console.log(multiply(3, 4));
multiply = function (a, b) { // 변수의 할당부는 원래 자리
return a + b;
};
어함수 선언문과 함수 표현식은 hoisting 과정에서 극명한 차이를 보임.
함수 선언문을 주의해야 하는 이유
...
console.log(sum(3, 4));
// 함수 선언문으로 짠 코드
// 100번째 줄 : 시니어 개발자 코드(활용하는 곳 -> 200군데)
// hoisting에 의해 함수 전체가 위로 쭉!
function sum (x, y) {
return x + y;
}
...
...
var a = sum(1, 2);
...
// 함수 선언문으로 짠 코드
// 5000번째 줄 : 신입이 개발자 코드(활용하는 곳 -> 10군데)
// hoisting에 의해 함수 전체가 위로 쭉!
function sum (x, y) {
return x + ' + ' + y + ' = ' + (x + y);
}
...
var c = sum(1, 2);
console.log(c);
만약 함수 표현식이었다면…?
...
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);
협업을 많이 하고, 복잡한 코드일 수록. 전역 공간에서 이루어지는 코드 협업일 수록 함수 표현식을 활용하는 습관을 들이도록
스코프
스코프 체인
식별자의 유효범위를 안에서부터 바깥으로 차례로 검색해나가는 것

outerEnvironmentReference(이하 outer)
그 두번째인 outer outer의 역할을 한 마디로 정의하자면
스코프 체인이 가능토록 하는 것(외부 환경의 참조정보)라고 할 수 있음
- 외부 환경의 참조정보 라는 말에 집중.
outer는 현재 호출된 함수가 선언될 당시(이 말이 중요)의 LexicalEnvironment를 참조.
참조한다는 말이 어려우면, 그 당시의 환경 정보를 저장한다. 정보로 이해해도 괜찮음
예를 들어, A함수 내부에 B함수 선언 → B함수 내부에 C함수 선언(Linked List)한 경우 어떻게 될까?
결국 타고, 타고 올라가다 보면 전역 컨텍스트의 LexicalEnvironment를 참조하게 됨
항상 outer는 오직 자신이 선언된 시점의 LexicalEnvironment를 참조하고 있으므로, 가장 가까운 요소부터 차례대로 접근 가능
결론 : 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에게만 접근이 가능
var a = 1;
var outer = function() {
var inner = function() {
console.log(a); // 이 값은 뭐가 나올지 예상해보자
var a = 3;
};
inner();
console.log(a); // 이 값은 또 뭐가 나올까요? 이유는?
};
outer();
console.log(a); // 이 값은 뭐가 나올까?
각각의 실행 컨텍스트는 LE 안에 record와 outer를 가지고 있고, outer 안에는 그 실행 컨텍스트가 선언될 당시의 LE정보가 다 들어있으니 scope chain에 의해 상위 컨텍스트의 record를 읽어올 수 있다.