모던 자바스크립트 Deep Dive 23장-실행 컨텍스트

HustleKang·2022년 4월 11일

소스코드의 타입

  • 전역 코드 : 전역 실행 컨텍스트 생성
  • 함수 코드 : 함수 실행 컨텍스트 생성
  • eval 코드 : eval 실행 컨텍스트 생성
  • 모듈 코드 : 모듈 실행 컨텍스트 생성

소스코드의 평가와 실행

소스코드의 평가를 마친 뒤 실행한다

  • 평가 : 실행 컨텍스트 생성, 선언문을 먼저 실행해 식별자를 렉시컬 환경의 환경 레코드에 등록
  • 실행 : 선언문을 제외한 코드가 실행됨, 소스코드가 실행되는데 필요한 변수나 함수의 참조는 실행 컨텍스트가 관리하는 스코프에서 찾아서 사용

실행 컨텍스트의 역할

  • 선언에 의해 생성된 모든 식별자들을 스코프를 구분하여 등록하고 상태 변화를 관리할 수 있어야한다
  • 스코프는 중첩 관계에 의해 스코프 체인을 형성해야 한다.
  • 현재 실행 중인 코드의 실행 순서를 변경,다시 되돌아갈 수 있어야 한다

실행 컨텍스트란 ?

  • 식별자를 등록하고 관리하는 스코프와 코드 실행 순서 관리를 구현한 내부 메커니즘
  • 모든 코드는 실행 컨텍스트를 통해 실행되고 관리된다
  • 렉시컬 환경 : 식별자와 스코프 관리
  • 실행 컨텍스트 스택 : 코드 실행 순서 관리

실행 컨텍스트 스택

코드의 실행 순서를 관리하는 스택
실행 컨텍스트 스택에 최상위에 있는 실행 컨텍스트는 현재 실행중인 코드의 실행 컨텍스트(running execution context)

const x = 1;

function foo() {
	const y = 2;
    
	function bar() {
		const z = 3;
		console.log(x + y + z);
	}
	bar():
}

foo();

  1. 전역 코드 평가
  2. 전역 코드 실행
  3. foo() 함수 코드 평가
  4. foo() 함수 코드 실행
  5. bar() 함수 코드 평가
  6. bar() 함수 코드 실행
  7. foo() 함수 코드로 복귀
  8. 전역 코드로 복귀
  9. 전역 실행 컨텍스트 제거

렉시컬 환경

  • 식별자와 식별자에 바인딩된 값, 상위 스코프에 대한 참조를 기록하는 자료구조
  • 스코프와 식별자를 관리한다
  • 키와 값을 갖는 객체 형태로 스코프를 생성
  • 스코프를 구분하여 식별자를 등록하고 관리하는 저장소 역할
  • 환경 레코드 : 스코프에 포함된 식별자를 등록, 식별자에 바인딩된 값 관리
  • 외부 렉시컬 환경에 대한 참조 : 상위 스코프인 외부 렉시컬 환경을 가르킴

실행 컨텍스트의 생성과 식별자 검색 과정

var x = 1;
const y = 2;

function foo (a) {
	var x = 3;
	const y = 4;
    
	function bar (b) {
		const z = 5;
		console.log(a + b + x + y + z);
	}
	bar(10);
}

foo(20); // 42

1. 전역 객체 생성

전역 코드가 평가되기 이전에 전역 객체 생성
빌트인 전역 프로퍼티,함수와 표준 빌트인 객체가 추가
동작 환경에 따른 호스트 객체 추가
전역 객체도 Object.prototype을 상속받음

2. 전역 코드 평가

  1. 전역 실행 컨텍스트 생성
  2. 전역 렉시컬 환경 생성
  3. 전역 환경 레코드 생성
    3-1. 객체 환경 레코드 생성
    3-2. 선언적 환경 레코드 생성
  4. this 바인딩
  5. 외부 렉시컬 환경에 대한 참조 결정

1. 전역 실행 컨텍스트 생성

비어있는 전역 실행 컨텍스트를 생성, 실행 컨텍스트 스택에 푸시

2. 전역 렉시컬 환경 생성

전역 렉시컬 환경을 생성하고 전역 실행 컨텍스트에 바인딩

3. 전역 환경 레코드 생성

객체 환경 레코드 + 선언적 환경 레코드 = 전역 환경 레코드

  • 객체 환경 레코드 (Object Environment Record)
    • var 키워드로 선언한 전역 변수
    • 함수 선언문으로 정의한 전역 함수
    • 빌트인 전역 프로퍼티, 빌트인 전역 함수, 표준 빌트인 객체
  • 선언적 환경 레코드 (Declaritive Environment Record)
    • let,const 키워드로 선언한 전역 변수

3-1. 객체 환경 레코드 생성

객체 환경 레코드는 BindingObject(전역 객체)와 연결됨
전역 코드의 평가 과정에서 var 키워드로 선언한 전역 변수와 함수 선언문으로 정의한 전역 함수는 전역 환경 레코드의 객체 환경 레코드에 연결된 BindingObject를 통해서 전역 객체의 프로퍼티와 메서드가 된다

var x = 1;
const y = 2;

function foo (a) {
	var x = 3;
	const y = 4;
    
	function bar (b) {
		const z = 5;
		console.log(a + b + x + y + z);
	}
	bar(10);
}

foo(20); // 42

x가 전역 객체의 프로퍼티로 등록된다, var이기 때문에 undefined로 초기화
foo()의 함수명과 동일한 foo라는 식별자를 전역 객체에 키로 등록하고 생성된 함수 객체를 즉시 할당
즉시 할당하기 때문에 함수 선언문 이전에도 호출이 가능해진다

3-2. 선언적 환경 레코드 생성

let,const로 선언한 전역 변수는 선언적 환경 레코드에 등록
변수 선언과 초기화단계가 분리되어 진행되기 때문에 런타임에 변수 선언문에 도달할 때까지 일시적 사각지대

4. this 바인딩

전역 환경 레코드의 [[GlobalThisValue]] 내부 슬롯에 전역 객체가 바인딩
전역 코드에서 this를 참조하면 [[GlobalThisValue]] 내부 슬롯에 바인딩된 전역 객체가 반환

5. 외부 렉시컬 환경에 대한 참조 설정

외부 렉시컬 환경에 대한 참조는 현재 평가중인 소스코드의 상위 스코프
지금은 전역 코드를 평가중이기 때문에 null이 할당된다

전역 코드 실행

x,y에 값이 할당되고 foo()가 실행됨
변수에 값을 할당하고 함수를 호출하려면 식별자를 찾아야한다
실행 중인 실행 컨텍스트에서 식별자를 찾는다
만약 현재 실행 중인 컨텍스트에서 식별자를 찾을 수 없다면 외부 렉시컬 환경에 대한 참조가 가르키는 상위 스코프로 이동해서 찾는다

foo 함수 코드 평가

  1. foo 함수 실행 컨텍스트를 생성, foo 함수의 렉시컬 환경이 완성된 다음 실행 컨텍스트 스택에 푸시
  2. 함수 렉시컬 환경을 생성
    2.1 함수 환경 레코드에 매개변수, arguments 객체, 함수 내부의 선언문 등록
    2.2 함수 환경 레코드 [[ThisValue]] 내부 슬롯에 this 바인딩
    2.3 foo함수 정의가 평가된 시점에 실행중이던 전역 실행 컨텍스트의 렉시컬 환경에 대한 참조가 외부 렉시컬 환경에 대한 참조에 할당

JS엔진은 함수 정의를 평가하여 함수 객체를 생성할 때 현재 실행 중인 실행 컨텍스트의 렉시컬 환경을 함수의 내부 슬롯 [[Environment]]에 저장한다

함수 렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 할당되는 것은 이미 전역 코드에서 함수에 대한 정의를 평가하여 함수의 내부 슬롯 [[Envoironment]]에 저장된 객체

foo 함수 코드 실행

var x = 1;
const y = 2;

function foo (a) {
	var x = 3;
	const y = 4;
    
	function bar (b) {
		const z = 5;
		console.log(a + b + x + y + z);
	}
	bar(10);
}

foo(20); // 42

평가가 끝난뒤 foo 함수 내부의 소스코드가 실행된다
지역 변수 x,y에 값이 할당되고 bar함수를 호출

bar 함수 코드 평가

bar 함수 내부로 코드의 제어권이 이동
bar 함수의 실행 컨텍스트와 렉시컬 환경을 만든 뒤 실행 컨텍스트 스택에 푸시

bar 함수 코드 실행

z에 5 할당
console.log(a + b + x + y + z);실행

  • console 식별자를 bar 함수의 렉시컬 환경에서 검색
  • 없으므로 상위 스코프 foo 함수의 렉시컬 환경에서 검색
  • 없으므로 상위 스코프 전역 렉시컬 환경에서 검색
  • 전역 렉시컬 환경의 객체 환경 레코드에 BindingObject에 바인딩된 전역 객체에서 console을 발견
  • console 식별자에 바인딩된 console객체에서 log 메서드를 찾는다, log메서드는 console 객체가 직접 소유한 정적 메서드
  • a + b + x + y + z 을 평가하기 위해 식별자를 렉시컬환경에서 찾는다
  • a + b + x + y + z 표현식을 평가한 값을 console.log 메서드에 전달하여 호출한다

bar 함수 코드 실행 종료

bar 함수 실행이 종료되고 실행 컨텍스트 스택에서 팝
실행 컨텍스트에서 bar 함수의 실행 컨텍스트가 제거됬다고 해서 bar 함수의 렉시컬 환경까지 즉시 소멸되지는 않는다
렉시컬 환경은 실행 컨텍스트에 의해 참조되지만 결국 독립적인 객체이다
객체를 포함한 모든 값은 누군가에 의해 참조되지 않을 때 가비지 컬렉터에 의해 소멸된다
bar 함수의 렉시컬 환경을 누군가는 참조하고 있다면 bar 함수의 렉시컬 환경은 소멸되지 않는다

foo 함수 코드 실행 종료

foo 함수 실행 종료되고 실행 컨텍스트 스택에서 팝

전역 코드 실행 종료

전역 코드도 더이상 실행할 코드가 없으므로 실행 컨텍스트 스택에서 팝
실행 컨텍스트 스택은 emtpy

실행 컨텍스트와 블록 레벨 스코프

let x = 1;

if (true){
	let x = 10;
    console.log(x); // 10
}

console.log(x); // 1

if 문이 실행 되면 if 문의 코드 블록을 위한 블록 레벨 스코프를 새롭게 생성
선언적 환경 레코드를 갖는 렉시컬환경을 만든다(외부렉시컬참조는 기존의 것을 가르킴)
기존에 렉시컬 환경이 가르키던 것을 새롭게 생성한 것으로 변경
if 문이 종료되면 원래 상태로 복구

이웅모, 『모던 자바스크립트 Deep Dive』, 위키북스(2021)

profile
grindin'

0개의 댓글