01.실행 컨텍스트란?
실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.
우선 스택stack과 큐queue의 개념을 살펴보자. 스택은 출입구가 하나뿐인 깊은 우물 같은 데이터 구조로 최신 데이터가 제일 먼저 나가고, 큐는 양쪽 모두 열려있는 파이프와 같은 구조로 먼저 들어간 데이터가 제일 먼저 나온다.
동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고, 이를 콜 스택call stack에 쌓아올렸다가, 가장 위에 쌓여있는 컨텍스트와 관련 있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장한다.
- VariableEnvironment : 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보. 선언 시점의 LexicalEnvironment의 스냅샷으로, 변경 사항은 반영되지 않음.
- LexicalEnvironment : 처음에는 VariableEnvironment와 같지만 변경 사항이 실시간으로 반영됨.
- ThisBinding: this 식별자가 바라봐야 할 대상 객체.
02. LexicalEnvironment
LexicalEnvironment는 함수 실행 컨텍스트, 전역 컨텍스트를 포함한 모든 javascript 코드 실행 시 생성된다. 이를 통해 javascript 엔진은 변수와 함수의 스코프, 접근 권한 및 스코프 체인을 관리하며, 코드 실행 중에 필요한 식별자에 접근할 수 있게 한다.
2-3-1 environmentRecord와 호이스팅
environmentRecord에는 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다. 컨텍스트를 구성하는 함수에 지정된 매개변수 식별자, 선언한 함수가 있을 경우 그 함수 자체, var로 선언된 변수의 식별자 등이 식별자에 해당한다. 컨텍스트 내부 전체를 처음부터 끝까지 쭉 훑어나가며 순서대로 수집한다.
호이스팅이란 '끌어올리다'라는 의미로 변수 정보를 수집하는 과정을 더욱 이해하기 쉬운 방법으로 대체한 가상의 개념이다. 자바스크립트 엔진이 실제로 끌어올리지는 않지만 편의상 끌어올린 것으로 간주하는 것이다.
함수 선언문과 함수 표현식
함수 선언문은 function 정의부만 존재하고 별도의 할당 명령이 없는 것을 의미하고, 반대로 함수 표현식은 정의한 function을 별도의 변수에 할당하는 것을 말한다. 함수 선언문의 경우 반드시 함수명이 정의돼 있어야 하는 반면, 함수 표현식은 없어도 된다. 함수명을 정의한 함수 표현식을 '기명 함수 표현식', 정의하지 않은 것을 '익명 함수 표현식'이라고 부르기도 하는데, 일반적으로 함수 표현식은 익명 함수 표현식을 말한다.
function a () { /* ... */ } // 함수 선언문. 함수명 a가 곧 변수명.
a(); //실행 ok
var b = function() { /* ... */ } // (익명) 함수 표현식. 변수명 b가 곧 함수명.
b(); //실행 ok
var c = function d () { /* ... */ } // 기명 함수 표현식. 변수명은 c, 함수명은 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;
}
2-3-2 스코프, 스코프체인, outerEnvironmentReference
스코프란 식별자에 대한 유효범위이다. 어떤 경계 A의 외부에서 선언한 변수는 A의 외부뿐 아니라 A의 내부에서도 접근이 가능하지만, A의 내부에서 선언한 변수는 오직 A의 내부에서만 접근할 수 있다. 이러한 스코프의 개념은 대부분의 언어에 존재한다. 이러한 '식별자의 유효범위'를 안에서부터 바깥으로 차례로 검색해나가는 것을 스코프 체인이라고 한다. 그리고 이를 가능케 하는 것이 바로 LexicalEnvironment의 두번째 수집 자료인 outerEnvironmentReference이다.
스코프 체인
outerEnvironmentReference는 현재 호출된 함수가 선언될 당시의 LexicalEnvironment를 참조한다. 과거 시점인 '선언될 당시'에 주목하자. '선언하다'라는 행위가 실제로 일어날 수 있는 시점이란 콜 스택 상에서 어떤 실행 컨텍스트가 활성화된 상태일 뿐이다. 어떤 함수를 선언(정의)하는 행위 자체도 하나의 코드에 지나지 않으며, 모든 코드는 실행 컨텍스트가 활성화 상태일 때 실행되기 때문이다. 여러 스코프에서 동일한 식별자를 선언한 경우에는 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에게만 접근 가능하다.
전역변수와 지역변수
전역 공간에서 선언한 변수는 전역변수이고, 함수 내부에서 선언한 변수는 무조건 지역변수이다. 코드의 안정성을 위해 가급적 전역변수 사용을 최소화하는 것이 좋다.
정리
- 실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다. 실행 컨텍스트는 전역 공간에서 자동으로 생성되는 전역 컨텍스트와 eval 및 함수 실행에 의한 컨텍스트 등이 있다. 살행 컨텍스트 객체는 활성화되는 시점에 VariableEnvironment, LexicalEnvironment, ThisBinding의 세 가지 정보를 수집한다.
- 실행 컨텍스트를 생성할 때는 VariableEnvironment와 LexicalEnvironment가 동일한 내용으로 구성되지만 LexicalEnvironment는 함수 실행 도중에 변경되는 사항이 즉시 반영되는 반면 LexicalEnvironment는 초기 상태를 유지한다. VariableEnvironment와 LexicalEnvironment는 매개변수명, 변수의 식별자, 선언한 함수의 함수명 등을 수집하는 environmentRecord와 바로 직전 컨텍스트의 LexicalEnvironment 정보를 참조하는 outerEnvironmentRecord와 바로 직전 컨텍스트의 LexicalEnvironment 정보를 참조하는 outerEnvironmentReference로 구성돼 있다.
- 호이스팅은 코드 해석을 좀 더 수월하게 하기 위해 environmentRecord의 수집 과정을 추상화한 개념으로, 실행 컨텍스트가 관여하는 코드 집단의 최상단으로 이들을 '끌어올린다'고 해석하는 것이다. 변수 선언과 값 할당이 동시에 이뤄진 문장은 '선언부'만을 호이스팅 하고, 할당 과정은 원래 자리에 남아있게 되는데, 여기서 함수 선언문과 함수 표현식의 차이가 발생한다.
- 스코프는 변수의 유효범위를 말한다. outerEnvironmentReference는 해당 함수가 선언된 위치의 LexicalEnvironment를 참조한다. 코드 상에서 어떤 변수에 접근하려고 하면 현재 컨텍스트의 LexicalEnvironment를 탐색해서 발견되면 그 값을 반환하고, 발견하지 못할 경우 다시 outerEnvironmentReference에 단김 LexicalEnvironment를 탐색하는 과정을 거친다. 전역 컨텍스트의 LexicalEnvironment까지 탐색해도 해당 변수를 찾지 못하면 undefined를 반환한다.
- 전역 컨텍스트의 LexicalEnvironment에 담긴 변수를 전역변수라 하고, 그 밖의 함수에 의해 생성된 실행 컨텍스트의 변수들은 모두 지역변수이다. 안전한 코드 구성을 위해 가급적 전역변수의 사용은 최소화하는 것이 좋다.
- this에는 실행 컨텍스트를 활성화하는 당시에 지정된 this가 저장된다. 함수를 호출하는 방법에 따라 그 값이 달라지는데, 지정되지 않은 경우에는 전역 객체가 저장된다.
'CoreJavaScript-study > dil' 카테고리의 다른 글
[DIL] 06 프로토타입 (4) | 2024.07.22 |
---|---|
[DIL] 05 클로저 (2) | 2024.07.15 |
[DIL] 04 콜백 함수 (1) | 2024.07.13 |
[DIL] 03 this (0) | 2024.07.10 |
[DIL] 01 데이터타입 (0) | 2024.07.02 |