모던 자바스크립트 Deep Dive를 공부하며 블로그에 정리해보는 포스팅입니다.
JS엔진에서 실행컨텍스트는 동작 원리를 담고 있는 핵심개념이며, 코드들이 실행되기 위한 환경이라 할 수 있습니다.
실행컨텍스트를 이해할 수 있다면 스코프 기반의 식별자 바인딩 개념, 호이스팅의 발생원인, 클로저의 동작방식, 태스크 큐와 동작하는 이벤트 핸들러, 비동기 처리의 동작 방식등을 설명할 수 있습니다.
소스코드
실행 컨텍스트를 생성하는 4가지 타입의 소스코드로 전역 코드, 함수 코드, eval 코드, 모듈 코드로 구분됩니다.
전역 코드는 전역변수를 관리하기 위한 전역 스코프를 생성합니다. var키워드로 선언된 전역 변수와 함수 선언문을 전역 객체의 프로퍼티와 메서드로 바인딩하고 참조하기 위해 전역객체와 연결합니다. 이를 위해 전역코드가 평가되면 전역 실행 컨텍스트가 만들어집니다.
함수 코드는 지역스코프를 생성합니다. 생선한 지역스코프를 전역 스코프에서 시작하는 스코프체인의 일원으로 연결해야합니다. 이를 위해 함수 코드가 평가되면 함수 실행 컨텍스트가 생성됩니다.
eval 코드는 strict mode (엄격모드)에서 독자적인 스코프를 생성합니다. eval 코드가 평가되면 eval 실행 컨텍스트가 생성됩니다.
모듈 코드는 모듈별 독립적인 모듈 스코프를 생성합니다. 이를 위해 모듈 코드가 평가되면 모듈 실행 컨텍스트가 생성됩니다.
위에서 계속 반복되는 평가는 무슨 의미를 지니고 있을까요?? 소스코드는 평가와 실행 두 단계를 거쳐 처리를 진행합니다.
소스코드 별로 각 단계에서 어떤 처리과정이 이뤄지는지 확인해보겠습니다.
소스코드 평가 & 실행 과정
평가 과정에서는 실행 컨텍스트를 생성하고 변수, 함수등의 선언문만 먼저 실행하여 생성된 변수나 함수 식별자를 키로 실행 컨텍스트가 관리하는 스코프(렉시컬 환경의 환경 레코드)에 등록합니다.
소스코드 평가과정이 끝나면 비로소 선언문을 제외한 소스코드가 순차적으로 실행되는 런타임이 시작되는데, 실행단계에서는 변수나 함수의 참조를 실행 컨텍스트가 관리하는 스코프에서 검색해서 취득합니다. 이후 변수 값의 변경 등 소스코드의 실행결과는 다시 실행 컨텍스트가 관리하는 스코프에 등록됩니다.
평가와 실행을 확인하기 좋게 코드로 작성해보겠습니다.
var x;
x = 1;
위에서 말씀드린대로 자바스크립트엔진은 소스코드를 평가하며 실행컨텍스트를 만들어냅니다. 소스코드 평가에서 var x 가 먼저 실행이 되면서 변수 식별자 x는 실행 컨텍스트가 관리하는 스코프에 등록되고 undefined로 초기화가 됩니다.
이후 실행과정에서 var x; 는 이미 평가과정에서 실행이 완료되었기 때문에 실행과정에서는 변수 할당문 x=1;만 실행됩니다. 이때 x변수에 값을 할당하려면 먼저 x 변수가 선된 변수인지 확인해야합니다.
이를 위해 실행컨텍스트가 관리하는 스코프에 등록이 되어있는 변수인지 먼저 확인을하고 등록되어있다면, 값을 할당하고 할당 결과를 실행컨텍스트에 등록하여 관리합니다.
실행컨텍스트를 생성하기 위해 평가와 실행이 이루어진다고 했는데, 예제를 통해 자바스크립트 엔진의 평가와 실행이 어떻게 이루어지는지 확인해보겠습니다.
// 전역 변수 선언
const x = 1;
const y = 2;
//함수정의
function foo(a) {
//지역변수 선언
const x = 10;
const y = 20;
//메서드 호출
console.log(a + x + y); // 130
}
//함수 호출
foo(100);
//메서드 호출
console.log(x + y); // 3
전역 코드 평가
전역에서 선언된 변수 선언문, 함수 선언문이 먼저 실행됩니다. 선언된 전역 변수와 전역 함수가 실행컨텍스트가 관리하는 전역 스코프에 저장됩니다. 이때 var 키워드로 선언된 전역 변수와 함수 선언문으로 정의된 전역 함수는 전역 객체의 프로퍼티와 메서드가 됩니다.
전역 코드 실행
런타임을 진행하며 전역 변수에 값이 할당되고 함수가 호출됩니다. 함수가 호출되면 순차적으로 실행되면 전역 코드의 실행이 중단되고 코드 실행 순서를 변경하여 함수 내부로 진입합니다.
함수 코드 평가
매개변수와 지역 변수 선언문이 먼저 실행되고, 실행컨텍스트가 관리하는 지역 스코프에 등록됩니다. 또한 함수 내부에서 지역 변수처럼 사용할 수 있는 arguments 객체가 생성되어 지역 스코프에 등록되고 this 바인딩도 결정됩니다.
함수 코드 실행
런타임이 시작됩니다. 순차적으로 코드가 진행되며, 매개변수(a)와 지역변수(x, y)에 값이 할당되고 이후 console.log 메서드가 호출됩니다. console.log 메서드를 호출하기 위해 식별자인 console을 스코프 체인을 통해 검색합니다. 함수 코드의 지역 스코프에는 console 메서드가 없기에 상위 스코프인 전역 스코프와 연결되어있어야합니다. 하지만 console 식별자는 스코프체인에 등록되어 있지않고, 전역 객체에 프로퍼티로 존재합니다. 여기서 알 수 있는 점은 전역 객체의 프로퍼티가 마치 전역 변수처럼 전역 스코프를 통해 검색이 가능하다는 점입니다.
다음으로 log 프로퍼티를 console 객체의 프로토 타입 체인을 통해 검색합니다. 이후 console.log 메서드에 인수로 전달된 표현식 a + x + y 가 평가됩니다. a, x, y 식별자는 스코프 체인을 통해 검색될 것 입니다.
그렇게 console.log의 실행이 종료되면 함수 코드 실행과정이 종료되고 함수 호출 이전으로 돌아가 전역 코드 실행을 계속합니다.
코드가 실행되려면 스코프를 구분짓고 식별자와 바인딩된 값이 관리되어야합니다. 또한 중첩 관계에 의해 스코프 체인을 형성하여 식별자를 검색할 수 있어야하며, 전역 객체의 프로퍼티도 전역 변수처럼 검색할 수 있어야합니다. 그리고 실행중인 코드의 실행순서를 변경 (함수호출에 의한 실행순서 변경)을 할 수 있어야하고 반대로 되돌아 갈 수 있어야합니다.
이 모든 것들을 관라하는 것이 실행 컨텍스트입니다.
다시말해 실행 컨텍스트는 소스코드를 실행하는데 필요한 환경을 제공하고 코드의 실행 결과를 실제로 관리하는 영역입니다.
구체적으로 말하면, 식별자 (변수, 함수, 클래스 등의 이름)을 등록하고 관리하는 스코프와 코드 실행 순서 관리를 구현한 내부 메커니즘으로, 모든 코드는 실행 컨텍스트를 통해 실행되고 관리됩니다.
코드 실행순서는 실행 컨텍스트 스택으로 관리되는데, 실행 컨텍스트 스택에 대해 알아보겠습니다.
실행 컨텍스트 스택
먼저 스택에 대한 개념을 알아야 실행 컨텍스트 스택에 대해 이해할 수 있습니다.
제 포스팅 중에 스택과 큐에 대해 정리한 글이 있으니 개념이해가 필요하시다면 읽고 오시는 것을 추천드립니다.
cosnt x = 1;
function foo () {
consnt y = 2;
function bar () {
cosnt z= 3;
console.log(x + y + z);
}
bar();
}
foo(); // 6
실행 컨텍스트 스택에 대해 알아보기 위해 위 예제를 가지고 왔습니다.
자바스크립트 엔진은 먼저 전역 코드를 평가하여 전역 실행 컨텍스트를 생성합니다. 그러던 중에 함수가 호출되면 함수 코드를 평가하러 갑니다. 함수 코드 내부에 들어가서 함수 평가를 진행하며, 함수 코드를 평가하여 함수 실행 컨텍스트를 생성합니다. 이때 생성된 실행 컨텍스트는 스택 자료구조로 관리됩니다. 이것이 실행 컨텍스트 스택입니다.
위 코드의 스택이 어떻게 쌓이는지 확인해보겠습니다.
1. [ ]
비어있는 실행 컨텍스트 스택
2. [ 전역 실행 컨텍스트 ]
먼저 전역 코드 평가가 이루어짐으로 실행 컨텍스트 스택에 전역 실행 컨텍스트 추가
3. [ 전역 실행 컨텍스트, foo 함수 실행 컨텍스트 ]
foo함수가 호출되어 foo함수 내부로 진입, 함수 코드 평가 -> foo 함수 실행 컨텍스트 추가
4. [ 전역 실행 컨텍스트, foo 함수 실행 컨텍스트, bar 함수 실행 컨텍스트 ]
bar 함수 호출 -> bar 함수코드 평가 -> bar 함수 실행 컨텍스트 생성
5. [ 전역 실행 컨텍스트, foo 함수 실행 컨텍스트 ]
bar 함수 코드 실행이 종료되면 실행 컨텍스트 스택에서 pop 됩니다.
6. [ 전역 실행 컨텍스트 ]
마찬가지로 foo 함수 코드 실행이 종료되면 실행 컨텍스트에서 pop 됩니다.
7. [ ]
전역 코드 실행이 종료됨에 따라 실행 컨텍스트 스택은 빈 상태가 됩니다.
이처럼 실행 컨텍스트 스택은 코드의 실행 순서를 관리해줍니다. 소스코드가 평가되면 실행 컨텍스트가 생성되고 실행 컨텍스트 스택의 최상위에 쌓이게 됩니다. 실행 컨텍스트 스택의 최상위에 존재하는 실행 컨텍스트는 언제나 현재 실행 중인 코드의 실행 컨텍스트입니다.
Q. 그럼 함수 호출이 안되면 해당 함수 코드 평가는 이루어지지않고 해당 함수의 실행 컨텍스트는 생성되지 않는것인가?
전역코드 평가가 이루어질 때, 선언문을 먼저 실행합니다. 전역 변수와 전역 함수가 실행 컨텍스트가 관리하는 전역 스코프에 등록됩니다. 이후 전역코드 실행을 통해 전역 변수에 값이 할당됩니다. 함수 호출이 이루어지면 해당 함수내로 진입하여 함수 코드 평가가 진행됩니다.
그렇다면 함수 호출이 이루어지지않는다면 함수 평가가 이루어지지않을텐데 해당 함수의 실행컨텍스트는 생성되지 않는걸까요?
A. 그렇습니다. 함수가 호출되지않으면 상위 레벨의 평가만 이루어지기 때문에 해당 함수의 실행 컨텍스트는 생성되지 않습니다.
Q. 함수 선언문으로 생성된 함수가 전역코드 평가시에 전역 환경레코드에 저장됩니다. 이때, 함수 선언문 내부의 전체 코드들도 함께 저장이 되는데 (함수 호이스팅) 함수 내부에 변수, console.log 등의 코드도 함께 저장이 되는건가요??
A. 아닙니다. '함수 선언문의 전체코드(내부코드)가 저장된다.' 라는 말은 함수의 이름, 참조(메모리 주소값), 스코프 정보(스코프 체인)등이 저장되는 것이며, 실제 내부 로직, 변수 명이 저장되는 것은 아닙니다. 함수가 호출됐을 때, 함수 내에서 평가단계를 통해 해당 레벨의 호이스팅과 실행단계의 할당이 이뤄지게 됩니다.
'JavaScript' 카테고리의 다른 글
[JS] 실행 컨텍스트 뿌수기 #3 함수 (0) | 2024.02.29 |
---|---|
[JS] 실행 컨텍스트 뿌수기 #2 렉시컬 환경 (0) | 2024.02.28 |
[JS] Scope chaning (0) | 2024.02.20 |
[JS] 배열 비교 (0) | 2024.01.13 |
JavaScript의 대표문법 정리 (초간단!) (0) | 2023.03.07 |