일시: 2022.10.9
참석자: 윤수지, 유효주, 조민재, 오예린
금주 스터디는 윤수지님께서 정리하신 내용을 공유해주셨습니다.
2. 실행 컨텍스트
실행 컨텍스트란?
- 스택 & 큐
- 실행 컨텍스트: 실행할 코드에 제공할 환경 정보들을 모아놓은 객체
- 동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고 이를 콜 스택(call stack)에 쌓아올렸다가, 가장 위에 쌓여있는 컨텍스트와 관련 있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장
- 실행 컨텍스트를 구성하는 방법: 전역공간, eval() 함수, 함수
// --------------------------------- (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 컨텍스트와 관련된 코드 진행
(2) : 콜 스택에 inner 실행 컨테스트 추가 —> inner 함수 내부 코드 진행 —> 함수 실행 종료되면 inner 컨텍스트 제거
(2) 다음 줄: outer 함수 실행 —> outer 컨텍스트 제거
(3) 다음 줄: 남은 코드 이어서 실행 —> 전역 컨텍스트 제거
어떤 실행 컨텍스트가 활성화될 때 자바스크립트 엔진은 해당 컨텍스트에 관련된 코드를 실행하는 데 필요한 환경 정보들을 수집해서 실행 컨텍스트에 저장
VariableEnvironment
: 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보. 선언 시점의LexicalEnvironment
의 스냅샷으로, 변경 사항은 반영되지 않음.LexicalEnvironment
처음에는VariableEnvironment
와 같지만 변경 사항이 실시간으로 반영됨ThisBinding
:this
식별자가 바라봐야 할 대상 객체
VariableEnvironment
실행 컨텍스트를 생성할 때 VariableEnvironment
에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LexicalEnvironment
를 만들고, 이후에는 LexicalEnvironment
를 주로 이용한다.
VariableEnvironment
와LexicalEnvironment
의 내부는environmentRecord
와outer-EnvironmentReference
로 구성되어 있음.
LexicalEnvironment
environmentRecord와 호이스팅(hoisting)
environmentRecord
- 현재 컨텍스트와 관련된 코드들의 식별자 정보들이 저장됨
- 컨텍스트 내부 전체를 처음부터 끝까지 훑어나가며 순서대로 수집
- 코드가 실행되기 전에 자바스크립트 엔진은 이미 해당 환경에 속한 코드의 변수명을 모두 알고 있는 상태
→ 이렇게 자바스크립트 엔진이 변수 정보를 수집하는 과정을 호이스팅(hoisting) 이라고 한다.
💡 참고
전역 실행 컨텍스트는 변수 객체를 생성하는 대신 자바스크립트 구동 환경이 별도로 제공하는 객체인 전역 객체(global object)를 사용한다.
브라우저의 window , Node.js의 global 객체 등이 전역 객체에 속하고, 이들은 자바스크립트 내장 객체(native object)가 아닌 호스트 객체(host object)로 분류된다.
호이스팅 규칙
environmentRecord
는 현재 실행할 컨텍스트의 대상 코드 내에 어떤 식별자들이 있는지에만 관심이 있고, 각 식별자에 어떤 값이 할당될 것인지는 관심이 없다. 따라서 변수를 호이스팅할 때 변수명만 끌어올리고 할당 과정은 원래 자리에 그대로 남겨둔다.
function a() {
var x = 1;
console.log(x); // (1)
var x;
console.log(x); // (2)
var x = 2;
console.log(x); // (3)
}
a();
위의 코드에서 호이스팅이 끝나고 나면 다음과 같은 형태로 바뀐다고 볼 수 있다.
function a() {
var x;
var x;
var x;
x = 1;
console.log(x); // (1) 1
console.log(x); // (2) 1
x = 2;
console.log(x); // (3) 1
}
a();
즉, 변수를 선언하는 부분이 먼저 실행되는데 이때 이미 선언된 변수 x 가 있으므로 중복된 선언은 무시되고, 이후 x에 1을 할당하기 때문에 (1), (2) 모두 1을 출력한다.
실행 컨텍스트는 변수의 경우에는 선언부와 할당부를 나누어 선언부만 끌어올리지만 함수 선언은 함수 전체를 끌어올린다.
function a() {
console.log(b); // (1) [Function: b]
var b = 'bbb';
console.log(b); // (2) 'bbb'
function b() {}
console.log(b); // (3) 'bbb'
}
a();
위의 경우 먼저 실행 컨텍스트에 의해 변수 b를 선언하고, 그 다음 함수 b를 선언한다. 이때 이미 선언된 변수 b가 있으므로 선언 과정은 무시되지만, 함수 선언의 경우 함수 전체를 끌어올리므로 함수는 별도의 메모리에 담기고 그 함수가 저장된 주솟값을 b와 연결된 공간에 저장한다.
따라서 (1) 에서는 함수 b가 출력되고, 이후 b에 다시 ‘bbb’ 값이 할당되기 때문에 (2), (3)은 ‘bbb’를 출력한다.
💡 참고
’함수 선언은 함수 전체를 끌어올린다’는 규칙 덕분에 함수를 선언한 위치와 무관하게 그 함수를 실행할 수 있게 되었지만 오히려 더 많은 혼란을 야기하기도 한다.
함수 선언문과 함수 표현식
- 함수 선언문(function declaration) : 함수의 정의부만 존재하고 할당 명령이 없는 것
- 함수 전체를 호이스팅
function a() {} // 함수 선언문. 함수명 a가 곧 변수명 a();
- 함수 표현식(function expression) : 정의한 function을 별도의 변수에 할당하는 것
- 변수 선언부만 호이스팅 → 함수를 하나의 값으로 취급
var b = function () {} // 익명 함수 표현식. 변수명 b가 곧 함수명 b(); var c = function d() {} // 기명 함수 표현식. 변수명은 c, 함수명은 d c(); d(); // error
- 기명 함수 표현식은 외부에서 함수명으로 함수 호출 불가능. 함수 내부에서 재귀함수를 호출하는 용도로 함수명(d)을 쓸 수 있음.
console.log(sum(1, 2)); // 3
console.log(multiply(3, 4)); // 'multiply is not a function' error
function sum (a, b) { // function declaration
return a + b;
}
var multiply = function (a, b) { // function expression
return a * b;
}
스코프, 스코프 체인, outerEnvironmentReference
- 스코프: 식별자에 대한 유효범위
- ES5까지 자바스크립트는 전역공간을 제외하면 오직 함수에 의해서만 스코프가 생성됨.
- 스코프 체인: 스코프를 안에서부터 바깥으로 차례로 검색해나가는 것
outerEnvironmentReference
: 스코프 체인을 가능하게 하는LexicalEnvironment
의 두 번째 수집 자료. 현재 호출된 함수가 선언될 당시의LexicalEnvironment
를 참조.
// --------------------------------- (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
inner
함수 내부에서는inner
,outer
, 전역 스코프 모두에 접근 가능outer
함수의console.log(a)
코드에서 먼저outer
컨텍스트의LexicalEnvironment
안의environmentRecord
에서a
를 탐색하고 해당 값이 없기 때문에outerEnvironmentReference
에 있는environmentRecord
(전역LexicalEnvironment
)를 탐색한다.a
가 있기 때문에 그 값인 1을 반환.- 변수 은닉화(variable shadowing): 변수는 무조건 스코프 체인 상에서 가까운 값부터 검색한다. 따라서
inner
함수 내부의LexicalEnvironment
에a
식별자가 존재하므로 스코프 체인 검색을 더 이상 진행하지 않고a
를 반환한다. 즉,inner
함수 내부에서a
변수를 선언했기 때문에 전역 공간에서 선언한 동일한 이름의a
변수에는 접근할 수 없다.
전역변수와 지역변수
- 전역변수(global variable) : 전역 공간에서 선언한 변수
- 지역변수(local variable) : 함수 내부에서 선언한 변수 → 함수 내부에서만 접근 가능.
this
실행 컨텍스트를 활성화하는 당시에 thisBinding
에 this
로 지정된 객체가 저장된다.
함수를 호출하는 방법에 따라 그 값이 달라지는데, 지정되지 않은 경우에는 전역 객체가 저장된다.
정리
- 실행 컨텍스트: 실행할 코드에 제공할 환경 정보들을 모아놓은 객체
- 전역 컨텍스트(전역 공간에서 자동으로 생성),
eval
, 함수 실행에 의한 컨텍스트 등 - 실행 컨텍스트 객체가 활성화되는 시점에
VariableEnvironment
,LexicalEnvironment
,ThisBinding
정보를 수집.
- 전역 컨텍스트(전역 공간에서 자동으로 생성),
VariableEnvironment
와LexicalEnvironment
는 동일한 내용으로 생성되지만,LexicalEnvironment
는 함수 실행 도중 변경되는 사항을 즉시 반영하고VariableEnvironment
는 초기 상태를 유지LexicalEnvironment
environmentRecord
: 매개변수명, 변수의 식별자, 선언한 함수의 함수명 등을 수집- 호이스팅: 변수 선언과 값 할당이 동시에 이뤄진 문장은 선언부만 호이스팅, 함수 선언문의 경우 함수 전체를 호이스팅
outerEnvironmentReference
: 바로 직전 컨텍스트의LexicalEnvironment
정보 참조- 스코프: 변수의 유효범위
- 전역변수: 전역 컨텍스트의
LexicalEnvironment
에 담긴 변수 / 지역변수: 함수에 의해 생성된 실행 컨텍스트의 변수
'4-1기 스터디 > Javascript-Typescript Deep Dive' 카테고리의 다른 글
[JS Deep Dive 스터디] 5주차 - 클로저 (0) | 2023.01.25 |
---|---|
[JS Deep Dive 스터디] 4주차 - 콜백 함수 (1) | 2023.01.25 |
[JS Deep Dive 스터디] 3주차 - this (0) | 2023.01.25 |
[JS Deep Dive 스터디] 1주차 - 데이터 타입 (0) | 2022.10.18 |
[JS Deep Dive 스터디] OT (0) | 2022.10.18 |
댓글