23장 실행 컨텍스트
❗❗실행 컨텍스트는 자바스크립트의 동작 원리를 담고 있는 핵심 개념이다❗❗
실행 컨텍스트를 바르게 이해하면 스코프를 기반으로, 식별자와 식별자에 바인딩 된 값(식별자 바인딩)을 관리하는 방식, 호이스팅이 발생하는 이유, 클로저의 동작 방식, 태스크 큐와 함께 동작하는 이벤트 핸들러와 비동기처리의 동작방식을 이해할 수 있다.
스코프, 식별자, 식별자 바인딩, 클로저, 호이스팅, 태스크 큐, 비동기처리 동작방식을 이해할 수있다
23.1 소스코드의 타입
4가지 타입의 소스코드 - 실행 컨택스트 생성 (ECMAScript)
소스코드 타입 | 설명 |
---|---|
전역 코드 | 전역에 존재하는 소스코드, 전역에 정의된 함수, 클래스는 포함하지 않음 |
함수 코드 | 함수 내부에 존재하는 소스코드, 함수 내부에 중첩된 코드, 클래스는 포함되지 않음 |
eval 코드 | 빌트인 전역함수인 eval 함수에 인수로 전달되어 실행되는 소스코드. |
모듈 코드 | 모듈 내부에 존재하는 소스코드, 모듈 내부의 함수, 클래스 등의 내부코드는 포함되지 않음 |
소스코드를 네가지로 분류하는 이유? 📌 소스코드의 타입에 따라 실행 컨택스트를 생성하는 과정과 관리 내용이 다르기 때문이다
✅ 전역코드, 함수코드, eval코드, 모듈코드는 평가 될 때 각각의 실행 컨택스트 생성.
1. 전역 코드
전역 코드가 평가되면 전역 실행 컨텍스트 생성
전역 변수를 관리하기 위해. 최상위 스코프인 전역 스코프 생성,
전역 객체와 연결 - var 키워드로 생성된 전역변수 - 함수 선언문으로 정의된 전역변수 전역 객체 프로퍼티와 메서드로 바인딩
2. 함수 코드
함수 코드가 평가되면 함수 실행 컨택스트가 생성됨
지역 스코프 생성,
지역 변수, 매개변수, arguments를 관리
생성한 지역스코프를 전역 스코프에서 시작하는 스코프 체인의 일원으로 연결
3. eval 코드
eval코드가 평가되면 eval 실행 컨택스트 생성
strict mode에서 독자적인 스코프 생성
모듈 코드
모듈 코드가 평가되면, 모듈 실행 컨택스트 생성
모듈별로 독립적인 모듈 스코프생성
23.2 소스코드의 평가와 실행
자바스크립트 엔진은 소스코드를 “소스코드의 평가”와 “소스코드의 실행” 두개의 과정으로 나누어 처리한다
소스코드의 평가
실행컨택스트 생성
변수, 함수등의 선언문만 먼저 실행
변수나 함수 식별자를 키로 실행 컨텍스트가 관리하는 스코프(렉시컬 환경의 환경 레코드)에 등록
✅ 소스코드 평가과정
✅ 소스코드평가 이후에 런타임이 실행된다 ( = 선언문 제외 소스코드가 순차적으로 실행된다)
예시
위 두개의 코드를 처리하는 과정
23.3 실행 컨텍스트의 역할
자바스크립트는 소스코드를 어떻게 평가하고 실행하나?
위와 같은 예제를 자바스크립트 엔진은 어떻게 평가, 실행하는지 살펴보면
1. 전역 코드 평가
전역 코드를 실행하기 앞서 전역 코드 평가 과정을 거친다
소스코드 평가 과정에서는 선언문만 먼저 실행함
전역 코드의 변수 선언문과 함수 선언문이 먼저 실행되고, 그 결과로 생성된 전역변수와 전역 함수가 실행컨텍스트가 관리하는 전역 스코프에 등록
var 키웓로 선언된 전역 변수와 함수 선언문으로 정의된 전역 함수는 전역 객체의 프로퍼티와 메서드가 된다
2. 전역 코드 실행
전역 코드 평가 과정이 끝나면 런타임이 시작되어 전역 코드가 순차적으로 실행되기 시작함
전역변수에 값이 할당되고 함수가 호출됨
함수가 호출되면 전역 코드의 실행을 일시 중단하고, 코드 실행 순서를 변경하여 함수 내부로 진입
3. 함수 코드 평가
함수 호출로 코드 실행순서가 변경되어 함수 내부로 진입하면, 함수 내부의 문을 실행하기 앞서 함수 평가 과정을 거침
매개변수와 지역 변수 선언문이 먼저 실행됨
생성된 매개변수와 지역변수가 실행 컨텍스트가 관리하는 지역 스코프에 등록됨
함수 내부에서 지역변수처럼 사용되는 arguments 객체가 생성되어 지역 스코프에 등록되고, this 바인딩도 결정됨
4. 함수 코드 실행
함수 코드 평가가 끝나면 런타임이 시작되어 함수 코드가 순차적으로 실행됨, 매개변수와 지역변수에 값이 할당되고, console.log 메서드가 호출됨(코드에 console.log)
console.log 메서드를 호출하기 위해, 식별자인 console을 스코프 체인을 통해 검색한다
이를 위해 함수 코드의 지역 스코프는 상위 스코프인 전역 스코프와 연결되어야 한다. console 식별자는 스코프체인에 등록된게 아니라 전역 객체에 프로퍼티로 존재한다 (전역 객체의 프로퍼티가 전역 변수처럼 전역 스코프를 통해 검색 가능해야한다)
log 프로퍼티를 console 객체의 프로토타입 체인을 통해 검색한다
console.log 메서드에 인수로 전달된 표현식 a + x + y가 평가된다, a,x,y 식별자는 스코프 체인을 통해 검색함
console.log 메서드 실행이 종료되면, 함수 코드 실행 과정이 종료되고, 함수 호출 이전으로 되돌아가 전역 코드 실행을 계속한다
⭐️요약
💡 코드가 실행되려면, 스코프, 식별자, 코드실행 순서 등의 관리가 필요하다
코드가 실행되려면, 스코프를 구분하여 식별자와 바인딩된 값이 구분되어야 한다.
중첩 관계에 의해 스코프 체인을 형성하여 식별자를 검색할 수 있어야 함
전역객체의 프로퍼티도 전역 변수처럼 검색할 수 있어야 한다
또한 함수 호출이 종료되면, 함수 호출 이전으로 돌아가기 위해 현재 실행중인 코드와 이전에 실행하던 코드를 구분하여 관리해야 함
실행 컨텍스트가 관리하는 것
선언에 의해 생성된 모든 식별자(변수, 함수, 클래스)를 스코프를 구분하여 등록하고 상태 변화(식별자에 바인딩 된 값의 변화)를 지속적으로 관리할 수 있어야함
스코프는 중첩 관계에 의해 스코프 체인을 형성해야 함
스코프 체인을 통해 상위 스코프로 이동하여 식별자를 검색할 수 있어야함
현재 실행중인 코드의 실행순서를 변경할수 있어야한다.
ex) 함수 호출에 의한 실행순서 변경
📌 실행 컨텍스트는 소스코드를 실행하는데 필요한 환경을 제공하고 코드의 실행 결과를 실제로 관리하는 영역 스코프 관리(식별자 등록 , 관리)와 실행 순서 관리를 구현한 내부 메커니즘이다.
📌 렉시컬 환경 + 실행 컨텍스트 스택
✅ 식별자와 스코프는 실행 컨텍스트의 렉시컬 환경으로 관리하고, 코드 실행 순서는 실행 컨텍스트 스택으로 관리한다
23.4 실행 컨텍스트 스택
예제로 실행 컨텍스트 스택 이해하기
💡 코드 실행 순서를 결정하는 실행 컨텍스트는 스택 자료구조로 관리된다
전역 코드 평가 - 전역 코드 실행컨텍스트 생성 - 전역코드 실행 - 함수 호출되면 함수 실행 컨텍스트 생성 - 함수 실행 - …. 이런식으로 코드가 실행되는데 실행되는 시간의 흐름에 따라 컨텍스트가 push / pop 된다
순서 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
bar함수 실행 컨텍스트 | ||||||
foo 함수 실행 컨텍스트 | foo함수 실행 컨텍스트 | foo함수 실행컨텍스트 | ||||
전역 실행컨텍스트 | 전역 실행컨텍스트 | 전역 실행컨텍스트 | 전역 실행컨텍스트 | 전역 실행컨텍스트 | ||
> | > | > | > | > | > | > |
💡 전역코드 { foo 코드 { bar 코드 }}
전역 코드 평가와 실행
전역 코드를 평가하여, 전역 실행 컨텍스트 생성
실행 컨텍스트 스택에 푸시
변수 x와 foo는 전역 실행 컨텍스트에 등록된다
전역코드 평가가 끝나면 전역 코드 실행
변수 x에 값이 할당되고, foo 함수 호출
foo 함수 코드 평가와 실행
함수 foo가 호출되면, 전역 코드 실행이 일시 중단되고, 코드 제어권이 foo 함수 내부로 이동함
자바스크립트 엔진이 foo 내부 코드를 평가
foo 함수 실행컨텍스트 생성,
실행 컨텍스트 스택에 foo 실행컨텍스트 푸시
foo 함수의 지역변수 y와 중첩 함수 bar가 foo 함수 실행컨텍스트에 등록됨
foo함수 평가가 완료되면 foo 함수 실행
지역변수 y에 값이 할당되고 중첩변수 bar가 호출됨
bar 함수 코드 평가와 실행
중첩 함수 bar가 호출되면, foo 함수 코드 실행이 일시 중단되고, 코드의 제어권이 bar 함수 내부로 이동
자바스크립트 엔진이 bar 내부 코드를 평가
bar 함수 실행컨택스트 생성,
실행컨택스트 스택에 bar 함수 실행컨택스트 푸시
bar 함수의 지역변수 z가 bar 함수 실행컨텍스트에 등록
bar 함수 평가가 완료되면 bar 함수 실행
지역변수 z에 값 할당, console.log 메서드 호출 후에 bar 함수 종료
foo 함수 코드로 복귀
bar함수가 종료되면, 코드의 제어권을 다시 foo함수로 이동
실행 컨텍스트 스택에서 bar 함수의 실행컨텍스트 pop으로 제거
foo 함수도 더이상 실행할게 없으므로, 종료됨
전역 코드로 복귀
foo함수가 종료되면, 코드의 제어권을 다시 전역코드로 이동
실행컨텍스트 스택에서 foo함수의 실행컨텍스트 pop으로 제거
전역코드도 더이상 실행할게 없으므로 종료,
실행컨텍스트 스택에서 전역 실행컨텍스트 pop으로 제거
💡 실행 컨텍스트 스텍은 이처럼, 코드 실행순서를 관리함, 실행 컨텍스트의 최상위에 있는 실행 컨텍스트는 언제나 현재 실행중인 코드의 실행컨텍스트이다 running execution context라고 부름 (실행중인 실행컨텍스트)
23.5 렉시컬 환경
📌 스코프와 식별자를 관리
실행 컨텍스트 스택 - 실행 순서 관리 렉시컬 환경 - 식별자와, 식별자에 바인딩 된 값, 상위스코프 참조 기록
렉시컬 환경 구성
렉시컬 환경은 객체 형태의 스코프를 생성
키와 값을 갖는 객체형태의 스코프 생성
식별자 키로 등록, 식별자에 바인딩 된 값 관리
💡 스코프를 구분하여 식별자를 등록하고 저장소 역할을 한다 💡 렉시컬 스코프의 실체
실행 컨텍스트와 렉시컬 환경의 관계
컴포넌트 이름 설명 환경 레코드 (Environmental Record)
스코프에 포함된 식별자 등록, 식별자에 바인딩 된 값 관리, 소스코드 타입에 따라 관리하는 내용이 다르다
외부 렉시컬 환경 참조 (OuterLexicalEnvironmental Reference)
상위 스코프를 가리킴 해당 실행 컨텍스트를 생성한 소스코드를 포함하는 상위 코드의 렉시컬 환경을 말한다 단방향 링크드 리스트인 스코프체인을 구현함
23.6 실행 컨텍스트의 생성과 식별자 검색 과정
예제를 통해 실행 컨텍스트와 코드 실행 결과 관리 확인해보기
23.6.1. 전역 객체 생성
전역 객체는 전역 코드가 평가되기 이전에 생성됨
전역 객체도 Object.prototypedmf 상속받는다
전역 객체도 프로토 타입 체인의 일원이다
23.6.2 전역 코드 평가 🔥❗️
##평가순서
소스코드가 로드 된 이후 자바스크립트 엔진은 다음과 같은 순서로 전역 코드를 평가한다
전역 실행 컨텍스트와 렉시컬 환경 생성 결과
🚩 평가 순서 세부내용
1. 전역 실행 컨택스트 생성
비어있는 실행 컨텍스트 생성, 실행 컨텍스트 스택에 푸시
2. 전역 렉시컬 환경 생성
전역 렉시컬 환경을 생성하고 실행 컨텍스트에 바인딩
전역 렉시컬 환경 생성 세부내용
2-1. 전역 환경 레코드 생성 🔥❗️
📌 전역 환경 레코드 = 객체 환경 레코드(var) + 선언적 환경 레코드(let, const)
전역 스코프(전역변수 관리), 전역 객체의 빌트인 프로퍼티, 빌트인 함수, 표준 빌트인 객체 제공
ES6 이전에는 전역 객체가 전역 환경 레코드의 역할을 수행
❗️ 전역에서 선언된 let, const 키워드 사용 시 전역적인 프로퍼티가- 아니라 개념적인 블록에 존재하게 되는데.. 이러한 관리를 위해 전역 환경 레코드는
객체 환경 레코드 + 선언적 환경 레코드로 구성
2-1-1. 객체 환경 레코드 생성
📌 객체 환경 레코드는 BindingObject와 연결, BindingObject는 var 또는 전역 환경에서 function 키워드들을 전역 객체(Window)에 연결
🔥🔥 예제 코드 체크
var x
생성과 초기화 동시에 진행, 변수 따라서 선언문 이전에도 참조 가능 (실행 이전에 평가가 일어나니까 - 호이스팅)
생성시 BindingObject를 통해 전역 객체에 변수 식별자 등록, undefined으로 초기화
var 키워드로 선언한 함수 표현식도 동일하게 동작한다
function foo()
함수 선언문으로 선언한 함수는 - 함수 이름을 식별자로 BindingObject를 통해 전역 객체에 키로 등록
키로 등록함과 동시에 생성된 함수 객체 즉시 할당 * 변수 호이스팅 -> undefined 먼저 할당, 함수 선언문 -> 함수 객체 먼저 할당 - 변수 호이스팅과 함수호이스팅의 차이
✅ 객체 환경 레코드 생성결과
2-1-2. 선언적 환경 레코드 생성
📌 let, const는 선언적 환경 레코드에 등록되고 관리된다 전역에서의 let, const는 전역 프로퍼티가 아니라 개념적인 블록 즉 전역의 선언적 환경 레코드 내에 존재
🔥🔥 예제 코드 체크
const y
전역 객체의 프로퍼티가 되지 않음, 따라서 window.y로 접근 불가능
생성과 초기화 분리해서 진행 변수가 undefined로 초기화 되어있지 않으므로 참조시 에러 발생
호이스팅은 발생하나, 실제로는 작동하지 않음, 초기화 전까지 일시적 사각 지대 (Temporal Dead Zone)
생성시 BindingObject를 통해 전역 객체에 변수 식별자 등록, undefined으로 초기화
🔥 TDZ 관련 예제
let 키워드로 선언한 변수가 호이스팅 되지 않는다면?
전역 변수를 참조해서, 1을 출력해야함
하지만 let 변수도 호이스팅이 일어나기 때문에 참조에러가 발생
블록 안의 console.log는 블록 안의 foo 참조
블록 안의 foo가 현재 TDZ 상태이므로 참조 에러(RefereneError) 발생
💡 객체 환경 레코드와 선언적 환경 레코드는 협력하여 스코프와 전역 객체를 관리
2-2. This 바인딩
전역 환경 레코드의 [[GlabalThisValue]] 내부 슬롯에 this가 바인딩,
전역 코드에서 this는 일반적으로 전역 객체를 가리킴, 전역 환경 레코드의 [[GlobalThisValue]]에는 window객체가 바인딩된다.
this 바인딩은 전역 환경 레코드와 함수 환경 레코드에만 존재함!!
this 바인딩은 전역환경 또는 함수환경에서만 가능
2-3. 외부 렉시컬 환경에 대한 참조 결정
현재 평가중인 소스코드 포함, 외부 소스코드의 렉시컬 환경을 가리킴
즉 상위 스코프를 가리킴, 단방향 링크드인 스코프 체인 구현
현재 평가 중인 소스코드는 전역코드임
따라서 전역 렉시컬 환경 - 외부 렉시컬 환경은 null이 할당됨
전역 렉시컬 환경이 스코프 체인의 종점에 존재한다@@
23.6.3 전역 코드 실행
전역 렉시컬 환경 생성 이후 전역 코드가 생성된다
변수 할당문 실행, x와 y에 값이 할당된다
foo 함수가 호출됨
🔥식별자 결정
변수 할당문, 함수 호출문 실행하기 전에 식별자를 먼저 결정해야 하는 어떤 방식으로 결정되는가?
현재 전역 실행 컨택스트 실행 중이므로, 전역 렉시컬 환경 안의 객체 환경 레코드, 선언적 환경 레코드 안에 x와 y foo가 모두 등록이 되어있다.
만약 식별자가 없었다면, 전역에서의 상위 스코프는 null 이므로, 참조 에러를 발생시킨다
23.6.4. foo 함수 코드 평가🔥❗️
예제 코드
x에 1 할당, y에 2 할당 이후 foo 함수를 실행하게 된다 이때 전역 코드의 실행을 일시 중지하고, foo 함수 내부로 코드의 제어권이 이동된다
함수 코드 평가는 아래 순서로 진행된다
함수 실행 컨텍스트 생성
foo함수 실행 컨텍스트를 생성함
함수 실행 컨텍스트는 함수 렉시컬 환경이 완성된 다음 실행 컨텍스트 스텍에 푸시된다
이때 foo함수 실행 컨텍스트는 실행 컨택스트 스택의 최상위로 올라감, (실행중인 컨텍스트)
함수 렉시컬 환경 생성
foo함수 렉시컬 환경(Function Lexical Environment)을 생성하고, foo 함수 실행 컨텍스트에 바인딩한다
함수 렉시컬 환경은 함수 환경 레코드 + 외부 렉시컬 환경에 대한 참조로 구성됨!! (기본 렉시컬 환경 구성 = 환경 레코드 + 외부 렉시컬 환경 참조)
2.1 함수 환경 레코드 생성
📌 함수 렉시컬 환경 - 함수 환경 레코드 생성할 때
매개변수, arguments 객체, 함수 내부의 지역변수와 중첩 함수 등록 및 관리
argumetns / foo 함수 내의 x, y, bar함수 / callee - 함수 환경 레코드에 등록
2.2 this 바인딩
📌 this는 함수 호출 방식에 따라 결정된다. 함수 호출 시에 함수 렉시컬 환경 내의 [[ThisValue]]에 바인딩된다
22장 this에 설명 되어있음
요약
일반함수 - 전역 객체 바인딩
객체 메서드 - 메서드를 호출한 객체 바인딩
생성자 함수 - 미래의 인스턴스 바인딩
apply,call,bind - 첫번째 인수로 전달해준 객체
2.3 외부 렉시컬 환경에 대한 참조 결정
📌 지금 실행하는 함수 "정의"가 평가된 시점의 실행컨텍스트의 렉시컬 환경의 참조가 할당된다 함수의 외부 렉시컬 환경 참조 함수가 정의된 행 컨텍스트의 렉시컬 환경을 참조
foo가 평가될 시점에서의 실행 컨텍스트는 전역 실행 컨텍스트이므로, 전역 렉시컬 환경을 참조함.
상위 스코프 결정 시 외부 렉시컬 참조를 활용한다
함수 객체 생성 시 함수의 상위 스코프 (= 외부 렉시컬 환경 참조, =함수 정의가 평가된 실행 컨텍스트의 렉시컬 환경)를 함수 객체의 내부슬롯 [[Environment]]에 저장한다.
23.6.5. foo 함수 실행
런타임 시작, 매개변수에 할당, 변수 할당 등 지역변수에 값이 할당된다 bar 함수 호출됨
식별자 결정을 할 때 함수 렉시컬 -> 외부 렉시컬 참조 순으로 식별자를 검색하고 값을 바인딩한다
23.6.6~7 bar 함수 코드 평가/실행
foo 함수를 평가 / 실행 한 것처럼 동일하게 동작
bar 함수 호출 시 내부 제어권 bar 함수로 이동
함수 평가
bar) 함수 실행 컨텍스트 생성 - 함수 렉시컬 환경 생성 - 환경 레코드 등록 - this 바인딩 - 외부 렉시컬 환경 참조 순으로 평가
외부 렉시컬 환경은, bar 함수 평가가 실행되는 foo 함수의 렉시컬 환경,,, 정의된 함수의 렉시컬 환경이라고 생각하면 될듯 하다
함수 실행
z에 값 할당 이후 console.log 실행
console.log 실행
console 식별자 스코프 체인에서 검색한다
현재 실행중인 렉시컬 환경 ->외부 렉시컬 ->외부 렉시컬 반복해서 console 식별자를 찾는다
bar 렉시컬 없음 -> foo 렉시컬 없음 -> 전역 렉시컬 -> 객체 환경 레코드의 BindingObject에서 찾게 됨
log 메서드 검색
console 객체의 프로토타입 체인을 통해 메서드를 검색
표현식 (a,b,x,y,z) 평가
렉시컬 -> 렉시컬 계속 참조하면서 식별자 a,b,x,y,z 를 계속해서 찾는다
찾은 이후 식을 평가하여 인수로 전달해준다.
log 메서드 호출
표현식 평가 이후 생성된 값을 console.log에 전달하여 호출함
23.6.8 bar 함수 코드 실행 종료
실행 컨텍스트에서 bar 함수가 종료되어도 bar 함수의 렉시컬 환경은 즉시 소멸하지 않음
실행 컨텍스트에 의해 참조되지만 렉시컬 환경은 독립적인 객체이므로, 누군가에 의해 참조되지 않을 때 가비지 컬렉터에 의해 소멸
bar 함수가 종료되어도, 누군가 bar 렉시컬 환경을 참조한다면 소멸되지 않는다
23.6.9 foo 함수 코드 실행 종료 / 23.6.10 전역 코드 종료
각각의 함수는 실행이 종료되면서 실행 컨텍스트 스택에서 팝 되어 제거된다,
전역 코드 까지 종료되면 실행 컨텍스트 스택에는 아무것도 남지 않는다.
23.7 실행 컨텍스트와 블록레벨 스코프
📌 if / for / while / try, catch 문에서는 블록 렉시컬 환경을 생성한다. 이때 생성된 블록 렉시컬 환경이 블록 레벨 스코프 메커니즘을 가능하게 한다
코드 블록이 실행 되었을 때, 블록 레벨 스코프 생성을 위해 렉시컬 환경 새롭게 생성
선언적 환경 레코드를 갖는 렉시컬 환경이 생성된다.
외부 참조 렉시컬 환경은 코드블록이 실행된 렉시컬 환경을 참조한다.
코드블록이 실행 될 때마다 렉시컬 환경을 생성
for문의 경우 매 반복마다 독립적인 렉시컬 환경을 생성, 식별자 값 유지
24장 클로저에 내용이 많다.
코드블록 실행이 종료되면, 이전의 렉시컬 환경으로 돌아감
var 변수는 함수 코드블록만 지역 스코프로 인정, let, const 변수는 모든 코드 불록 지역 스코프로 인정
`
Last updated