일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- frontend
- 프론트엔드
- React Query
- 수코딩
- 웅진씽크빅
- 프론트엔드 개발
- 개발
- React
- tanstack query
- 유데미
- 공식문서
- Server State
- TypeScript
- 프로젝트캠프
- sucoding
- 리액트프로젝트
- 상태 관리 라이브러리
- STATE
- 스나이퍼팩토리
- Today
- Total
yunicornlab
JavaScript의 핵심! 실행 컨텍스트 이해하기 본문
모던 자바스크립트 딥다이브 책에 있는 실행 컨텍스트를 공부하면서
예시로 나온 코드 일부를 Gif로 직접 만들어봤다.
실행 컨텍스트 동작 애니메이션
실행 컨텍스트 동작 세부
예시 코드에는 전역 스코프에 선언된 변수 x, y와 함수 foo가 있고, 함수 foo 안에 지역 변수 x, y가 선언되어있다.
코드를 실행하면, 인터프리터가 코드를 한줄 한줄 실행하기 전에, 먼저 "평가" 단계를 거친다.
큰 순서로는 "전역 코드" -> "함수 코드" 이고, 매 단계마다 "평가 단계"와 "실행 단계"로 구분되어 진행된다.
평가 단계에서는 선언된 변수나 함수를 먼저 렉시컬 환경에 등록하고 (그렇기 때문에 호이스팅이 발생한다.)
실행 단계에서는 인터프리터가 코드를 한 줄 한줄 실행한다.
항상 전역 코드 부터 시작한다. 그래서 "전역 코드 평가 단계"가 먼저 시작된다.
👆🏻 위 그림의 과정
1. 전역 실행 컨텍스트를 생성한다
➡︎ 2. 비어있던 실행 컨텍스트 스택에 push한다. 이때, 전역 실행 컨텍스트가 최상위 컨텍스트가 된다.
➡︎ 3. 전역 렉시컬 환경을 생성한다.
➡︎ 4. 전역 렉시컬 환경을 전역 실행 컨텍스트에 바인딩한다.
👆🏻 위 그림의 과정
1. 전역 환경 레코드를 생성한다.
➡︎ 2. var 키워드로 변수 x를 선언했기 때문에 객체 환경 레코드를 생성한다.
➡︎ 3. 바인딩된 전역 객체에 변수 식별자를 키로 등록한 다음, var 키워드로 선언했기 때문에 undefined로 초기화를 동시에 진행해준다.
(호이스팅 발생. var로 선언한 변수는 선언과 동시에 초기화가 진행된다.)
➡︎ 4. 전역에 선언된 함수 foo도 전역 객체에 key로 등록하고, 생성된 함수 객체를 즉시 할당한다.
전역 환경 레코드는 객체 환경 레코드와 선언적 환경 레코드로 구성된다.
var 키워드로 선언된 변수나 함수 선언문은 객체 환경 레코드에서 관리되고,
let이나 const 키워드로 선언된 변수는 선언적 환경 레코드에서 관리된다.
👆🏻 위 그림의 과정
1. const 키워드로 변수 y를 선언했기 때문에 선언적 환경 레코드를 생성한다.
➡︎ 2. 변수 y를 등록하고 초기화는 진행하지 않는다.
(let과 const는 선언과 초기화가 동시에 진행되지 않기 때문이다.)
(호이스팅 발생. 단, let과 const의 초기화는 실제 코드가 실행되면서 해당 선언문에 도달했을 때 진행된다. 이 사이가 TDZ구간이다.)
전역 환경 레코드는 객체 환경 레코드와 선언적 환경 레코드로 구성된다.
var 키워드로 선언된 변수나 함수 선언문은 객체 환경 레코드에서 관리되고,
let이나 const 키워드로 선언된 변수는 선언적 환경 레코드에서 관리된다.
👆🏻 위 그림의 과정
1. 전역 환경 레코드의 [[GlobalThisValue]] 내부 슬롯에 this가 바인딩된다.
➡︎ 2. this가 전역 객체를 가리킨다. (일반적으로 전역 코드에서 this는 전역 객체를 가리킨다.)
👆🏻 위 그림의 과정
1. 외부 렉시컬 환경에 대한 참조
➡︎ 2. 전역 코드를 포함하는 상위 소스코드는 없기 때문에 null을 할당한다.
이제 인터프리터가 코드를 한줄 한줄 실행하게 된다. 먼저, 전역 코드부터 실행한다.
👆🏻 위 그림의 과정
1. var x = 1을 실행하기 위해, 변수 x를 검색한다.
➡︎ 2. undefined로 초기화되어있던 변수 x에 1을 재할당한다.
👆🏻 위 그림의 과정
1. const y = 2를 실행하기 위해, 변수 y를 검색한다.
➡︎ 2. 변수 y에 2를 할당한다.
전역 코드에서 foo(20);이라는 함수 호출문을 만났다.
foo 함수가 호출되면 전역 코드 실행은 임시 중단되고, 코드의 제어권이 foo 함수 내부로 이동한다.
그리고 foo 함수 코드 평가 단계와 foo 함수 코드 실행 단계를 거친 후 다시 전역 코드 실행으로 돌아간다.
👆🏻 위 그림의 과정
1. foo 함수 실행 컨텍스트를 생성한다
➡︎ 2. 실행 컨텍스트 스택에 push한다. 이때, foo 함수 실행 컨텍스트가 최상위 컨텍스트가 된다.
➡︎ 3. foo 함수 렉시컬 환경을 생성한다.
➡︎ 4. foo 함수 렉시컬 환경을 foo 함수 실행 컨텍스트에 바인딩한다.
👆🏻 위 그림의 과정
1. 함수 환경 레코드를 생성한다.
➡︎ 2. 함수 환경 레코드에 매개변수, arguments 객체, 함수 내부에서 선언한 지역 변수와 중첩 함수를 등록한다.
➡︎ 3. 이때 지역 변수 x는 var 키워드로 선언되었으므로 undefined로 먼저 초기화를 진행해준다.
함수 환경 레코드에서는 객체 환경 레코드와 선언적 환경 레코드로 구분하지 않는다.
즉, var 키워드로 선언하든지 함수 선언식이든지 let이나 const 키워드로 선언하든지
상관없이 함수 환경 레코드에 같이 관리된다.
👆🏻 위 그림의 과정
1. 함수 환경 레코드의 [[ThisValue]] 내부 슬롯에 this가 바인딩된다.
➡︎ 2. this가 전역 객체를 가리킨다.
(foo 함수가 일반 함수로 호출되었으므로 this는 전역 객체를 가리킨다.)
([[ThisValue]] 내부 슬롯에 바인딩될 객체는 함수 호출 방식에 따라 결정된다.)
👆🏻 위 그림의 과정
1. 외부 렉시컬 환경에 대한 참조
➡︎ 2. 전역 실행 컨텍스트의 렉시컬 환경의 참조가 할당된다.
(foo 함수는 전역 코드에 정의된 전역 함수이므로, foo 함수 코드를 포함하는 상위 소스코드는 전역 코드이다.)
foo 함수를 인터프리터가 한줄 한줄 실행하기 시작한다.
👆🏻 위 그림의 과정
1. var x = 3을 실행하기 위해 변수 x를 검색한다. (해당 함수 환경 레코드에 없으면 스코프 체인을 따라 상위 스코프로 이동해서 찾는다.)
➡︎ 2. 변수 x에 3을 재할당한다.
➡︎ 3. const y = 4를 실행하기 위해 변수 y를 검색한다.
(해당 함수 환경 레코드에 없으면 스코프 체인을 따라 상위 스코프로 이동해서 찾는다.)
➡︎ 4. 변수 y에 4를 할당한다.
foo 함수 내부의 코드를 모두 실행했기 때문에 함수 코드 실행을 종료한다.
foo 함수 실행 컨텍스트를 실행 컨텍스트 스택에서 pop하여 제거한다.
foo 함수 호출로 인해 임시 중단되었던 전역 코드 실행을 재개한다.
하지만, 전역 코드도 더이상 실행할 것이 없으므로 실행을 종료한다.
전역 실행 컨텍스트를 실행 컨텍스트 스택에서 pop하여 제거한다.
실행 컨텍스트 스택은 비어있게 된다.
'Frontend > JavaScript' 카테고리의 다른 글
화살표 함수 사용을 위한 문법 (0) | 2024.12.11 |
---|---|
JavaScript의 특징 - 멀티 패러다임 언어 (2) | 2024.07.22 |
JavaScript의 특징 - 동적 타입 언어 (0) | 2024.07.17 |
JavaScript의 특징 - 싱글 스레드 언어 (0) | 2024.07.15 |
JavaScript의 특징 - 인터프리터 언어 (0) | 2024.07.14 |