코어 자바스크립트를 읽으면서 공부한 내용을 저의 입맛에 맞춰 정리하였습니다.
혹시 이 글을 읽게 되시면 잘못된 내용이 있을 수 있으니 비판적인 시각으로 읽으시는 걸 추천드립니다.
실행 컨텍스트 (Execution Context)
실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체입니다.
자바스크립트는 동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고, 이를 콜 스택에 쌓아 올렸다가, 가장 위에 있는 컨텍스트와 관련 있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장합니다.
이때, 자바스크립트 엔진은 해당 컨텍스트와 관련된 코드들을 실행하는데 필요한 정보를 수집하여 실행 컨텍스트 객체에 저장하며, 저장되는 정보는 아래와 같습니다.
- VariableEnvironment
- 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보, 선언 시점의 LexicalEnvironment의 스냅샷
- VariableEnvironment의 담기는 내용은 LexicalEnvironment와 같지만 실행 시의 스냅샷을 유지한다는 차이점이 있습니다.
- 실행 컨텍스트를 생성할 때 VariableEnvirionment에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LexicalEnvironment를 만들고, 이후에는 LexicalEnvironment를 주로 활용하게 됩니다
- LexicalEnvironment
- 처음에는 VariableEnvironment와 같지만 변경 사항이 실시간으로 반영됨.
- 이 책에서는 LexicalEnvironment를 콘텍스트를 구성하는 환경정보들을 사전에 접하는 느낌으로 모아놓은 것'이라고 설명해주고 있습니다.
- ThisBinding
- this 식별자가 바라봐야 할 대상 객체
- 이 내용에 관해서는 다른 장에서 집중적으로 다룬다 합니다.
VariableEnvironment와 LexicalEnvironment의 내부는 environmentRecord와 outer-EnvironmentReference로 구성돼 있습니다.
EnvironmentRecord와 호이스팅
Environment Record에는 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장됩니다.
- 컨텍스트를 구성하는 함수에 지정된 매게변수 식별자
- 선언한 함수가 있을 경우 그 함수 자체
- var로 선언된 변수의 식별자
컨텍스트 내부 전체를 처음부터 끝까지 훑어나가며 순서대로 수집합니다. 여기서 호이스팅(Hoisting)이라는 개념이 등장하게 되는데 자바스크립트는 아직 코드가 실행되기 전임에도 해당 환경의 속한 코드의 변수명 들을 알게 되는 현상이 생긴 것입니다.
이를 추상적으로 생각하면, 변수 정보를 수집하는 과정에서 마치 자바스크립트가 변수명들을 위로 끌어올린 것처럼 느껴지고 이를 호이스팅(끌어올리는 것)이라고 합니다.
이런 추상적인 개념을 조금 더 이해하기 쉽게 변환 과정을 추가하면 아래와 같습니다.( 자바스크립트는 이런 과정을 거치진 않습니다.)
- 변경 전
- function a() { console.log(b); // (1) var b = 'bbb'; // 수집 대상 1(변수 선언) console.log(b); // (2) function b() {} // 수집 대상 2(함수 선언) console.log(b); // (3) } a();
- 변경 후
- function a() { var b; var b = function b() {}; // ← 바뀐 부분 console.log(b); // (1) b = 'bbb'; console.log(b); // (2) console.log(b); // (3) } a();
결국 위 코드는 호이스팅에 의해 (1) 번은 함수 자체를 출력하고, (2), (3)에서는 'bbb'를 출력하게 됩니다.
- environmentRecord는 현재 실행될 콘텍스트의 대상 코드 내에 어떤 식별자가 있는지에만 관심이 있습니다.
- 호이스팅에서 함수 선언문과 함수 표현식 역시 차이가 있습니다.
- 함수 선언문은 전체가 호이스팅 되지만 함수 표현식은 변수 선언부만 호이스팅 됩니다.
- 그 이유는, 함수를 다른 변수에 값으로써 '할당'한 것이 함수 표현식이기 때문이고, 호이스팅은 변수 할당에는 전혀 관심이 없기 때문입니다.
- 이런 이유로 함수 선언식이 불리할 수 있는데, 만약에 같은 식별자를 가진 함수 두 개가 선언됐을 경우, 일반적으로 선언된 위치에 따라 작동이 달라질 것을 기대하는데, 호이스팅에 의해서 모든 함수가 하나의 작동되게 됩니다.
추가, 함수 선언문과 함수 표현식
- 함수 선언문 : fuction 정의 부만 존재하고 별도의 할당 명령이 없는 것
- 함수 표현식 : fuction을 별도의 변수에 할당하는 것
- 기명 함수 표현식 : 함수명을 정의한 표현식
- 익명 함수 표현식 : 함수명을 정의하지 않은 표현식 (일반적인 함수 표현식)
function a() {
/* ... */
} // 함수 선언문. 함수명 a가 곧 변수명.
a(); // 실행 OK.
var b = function() {
/* ... */
}; // (익명) 함수 표현식. 변수명 b가 곧 함수명.
b(); // 실행 OK.
var c = function d() {
/* ... */
}; // 기명 함수 표현식. 변수명은 c, 함수명은 d.
c(); // 실행 OK.
d(); // 에러!
outerEnvironmentReference, 스코프, 스코프 체인
스코프란 식별자에 대한 유효 범위입니다. ES5까지에서는 함수에 의해서만 스코프가 생성됐지만 ES6부터 블록에서도 스코프 경계가 발생합니다.
'식별자의 유효 범위'를 안에서부터 바깥으로 차례로 검색해나가는 것을 스코프 체이이라고 합니다. 그리고 이를 가능케 하는 것이 바로 LexicalEnvironment의 두 번째 수집자료인 outerEnvironmentReference입니다.
스코프 체인
outerEnvironmentReference는 현재 호출된 함수가 선언될 당시의 LexicalEnvironment를 참조합니다.
연결 리스트의 형태로 가장 가까운 순서대로 LexicalEnvironment를 참조하며, 이런 구조적 특성 덕분에 여러 스코프에서 동일한 식별자를 선언한 경우에는 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근 가능합니다.
즉 함수 내부에서 a변수를 선언한다면 전역 공간에서 선언한 동일한 이름의 변수 a에는 접근할 수 없게 되고 이를 **변수 은닉화**라고 합니다.
정리
실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이며 VariableEnvironment, LexicalEnvironmet, ThisBinding의 세 가지 정보를 수집합니다.
VariableEnvironment와 LexicalEnvironment는 동일한 내용으로 구성되지만 LexicalEnvironment는 함수 실행 도중에 변경되는 사항이 즉시 반영되는 차이가 있습니다.
VE와 LE는 매개변수명, 변수 식별자, 선언한 함수의 함수명을 수집하는 environmentRecord와 바로 직전 컨텍스트의 LE정보를 참조하는 outerEnvironmetReference로 이루어져 있습니다.
호이스팅은 코드 해석을 수월하기 위해 environmentRecord에서 수집과정을 추상화 한 개념으로 변수 선언과 값 할당이 동시에 이뤄진 문장은 '선언부'만 호이스팅 합니다. 이에 할당 과정은 그 자리에 남게 되는데 함수 표현식과 선언문의 차이가 발생합니다.
스코프는 변수의 유효 범위를 말하며 o.e.r은 해당 함수가 선언된 위치의 LE를 참조합니다.
'CS' 카테고리의 다른 글
[JS] 콜백함수 (feat : 코어 자바스크립트) (0) | 2022.12.16 |
---|---|
[JS] this (feat : 코어 자바스크립트) (0) | 2022.12.15 |
[JS] undefined와 null (feat : 코어자바스크립트) (0) | 2022.12.13 |
[JS] 기본형데이터와 참조형데이터 (feat : 코어자바스크립트) (0) | 2022.12.12 |
[JS, React] ref, useRef (0) | 2022.08.25 |