[Javascript] 실행 컨텍스트 복습

소스코드의 평가와 실행

- 소스코드 평가

  - 실행 컨텍스트 생성

  - 변수, 함수 등의 선언문 실행 -> 렉시컬 환경의 환경 레코드에 등록

 

- 소스코드 실행 (런타임)

  - 실행에 필요한 정보를 렉시컬 스코프에서 검색

 

실행 컨텍스트의 역할

1. 코드 실행 순서 관리

  - 실행 컨텍스트 스택으로 관리

  - 현재 실행 중인 코드의 실행 순서를 변경 가능(ex 함수 호출에 의한 실행 순서 변경)

 

2. 스코프 관리(식별자 관리) 

  - 렉시컬 환경으로 관리

  - 선언에 의해 생성된 모든 식별자를 스코프를 구분하여 등록

  - 스코프는 중첩 관계에 의해 스코프 체인을 형성 -> 상위 스코프로 이동하며 식별자 검색 가능

 

실행 컨텍스트 스택

> 코드의 실행 순서를 관리

> 소스코드 평가 -> 실행 컨텍스트 생성 -> 실행 컨텍스트 스택에 push

> 실행 컨텍스트 최상위에 존재하는 실행 컨텍스트는 현재 실행 중인 코드의 실행 컨텍스트이다.

실행 컨텍스트 스택

 

렉시컬 환경

> 식별자와 식별자에 바인딩된 값, 상위 스코프에 대한 참조를 기록

> 스코프와 식별자를 관리

렉시컬 환경과 스코프 체인

- 구성요소

  - 환경 레코드: 식별자를 등록하고 등록된 식별자에 바인딩된 값을 관리하는 저장소

  - 외부 렉시컬 환경에 대한 참조: 상위 스코프의 렉시컬 환경에 대한 참조, 스코프 체인이 구현될때 이 경로를 이용 (단방향 링크드 리스트)

 

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

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. 전역 객체 생성

  - 전역 코드 평가 이전에 생성

  - 전역 객체에 빌트인 전역 프로퍼티, 빌트인 전역 함수, 표준 빌트인 객체 추가

  - 호스트 객체 포함(동작 환경에 따라 바뀜)

 

2. 전역 코드 평가

 

전역 코드 평가 순서

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

  2. 전역 렉시컬 환경 생성

    2-1. 전역 환경 레코드 생성

    2-2. this 바인딩

    2-3. 외부 렉시컬 환경에 대한 참조 결정

 

1. 전역 실행 컨텍스트 생성: 비어있는 전역 실행 컨텍스트 생성 -> 실행 컨텍스트 스택에 push

2. 전역 렉시컬 환경 생성: 렉시컬 환경 생성 -> 전역 실행 컨텍스트에 바인딩 (참조 연결)

2-1. 전역 환경 레코드 생성: 객체 환경 레코드, 선언적 환경 레코드 바인딩

  - 객체 환경 레코드: BindingObject를 통해 var로 선언한 변수, 함수 선언문이 바인딩됨 (window를 통해 참조 가능)

  - 선언적 환경 레코드: let, const 로 선언한 변수는 여기에 저장됨. 전역 객체(window)의 프로퍼티가 아닌 이유임

2-2. this 바인딩

  - 전역 환경 레코드의 [[GlobalThisValue]] 내부 슬롯에 this가 바인딩 됨 -> 일반적으로 전역 코드의 this는 전역 객체를 가리킴

2-3. 외부 렉시컬 환경에 대한 참조 결정

  - 상위 스코프를 가리킴 (단방향 링크드 리스트인 스코프 체인 구현)

  - 전역 렉시컬 환경의 외부 렉시컬 환경에 대한 참조는 null (스코프 체인의 종점)

 

3. 전역 코드 실행

  - 변수 할당문 실행 -> x,y에 값 할당, foo 호출

  - 변수 할당문, 함수 호출문을 실행하려면 어느 스코프의 식별자를 참조하면 되는지 식별자 결정을 해야 한다.

  - 식별자 결정: 실행 중인 실행 컨텍스트에서 식별자를 검색 -> 없다면, 외부 렉시컬 환경에 대한 참조를 타고 상위 스코프로 이동 -> 그래도 없다면 참조 에러 발생 (스코프 체인의 동작 원리)

 

4. foo 함수 코드 평가

함수 코드 평가 순서

  1. 함수 실행 컨텍스트 생성

  2. 함수 렉시컬 환경 생성

    2-1. 함수 환경 레코드 생성

    2-2. this 바인딩

    2-3. 외부 렉시컬 환경에 대한 참조 결정

 

1. 함수 실행 컨텍스트 생성: foo 함수 실행 컨텍스트 생성

  - 함수 실행 컨텍스트는 함수 렉시컬 환경이 완성된 이후 실행 컨텍스트 스택에 push 함!

2. 함수 렉시컬 환경 생성: foo 함수 렉시컬 환경 생성 -> 함수 실행 컨텍스트에 바인딩

2-1. 함수 환경 레코드 생성

  - 매개변수, arguments 객체, 함수 몸체에서 선언된 식별자를 등록, 관리

  - 함수 환경 레코드는 선언적 환경 레코드의 서브 타입이다.

  - 따라서 var와 let,const 로 선언된 변수가 따로 바인딩되는게 아님!
      -> var로 선언한 변수도 함수 블록 내에 갇히게 되는 이유 == 함수 내부에서 선언한 var 변수가 전역 객체에 바인딩 되지 않는 이유

2-2. this 바인딩

  - 함수 황경 레코드의 [[ThisValue]] 내부 슬롯에 this가 바인딩 됨 -> 함수 호출 방식에 따라 결정

  - foo는 일반 함수로 호출되었으므로 this는 전역 객체를 가리킴 -> [[ThisValue]] 내부 슬롯에 전역 객체 바인딩

  - foo 함수 내부에서 this 참조 시 [[ThisValue]] 에 바인딩된 값이 반환된다

 

2-3. 외부 렉시컬 환경에 대한 참조 결정

  - foo 함수 정의가 평가되 시점에 실행중인 실행 컨텍스트의 렉시컬 환경 === 전역 실행 컨텍스트의 렉시컬 환경

  - 전역 실행 컨텍스트 렉시컬 환경의 참조 바인딩

  - [[Environment]] 에 저장된 값 === 외부 렉시컬 환경에 대한 참조 -> 렉시컬 스코프를 구현하는 매커니즘 -> 클로저 이해하는데 중요한 단서

 

5. foo 함수 코드 실행

  - 식별자 결정을 위해 식별자 검색 -> 검색된 식별자에 값을 바인딩

 

6. bar 함수 코드 평가

  - foo 함수 평가와 동일한 과정을 거쳐 bar 함수의 실행 컨텍스트와 렉시컬 환경이 생성

 

7. bar 함수 코드 실행

  - 식별자 결정을 위한 식별자 검색 -> 값 할당

  - console.log(a+b+x+y+z) 실행

    1. console 식별자 검색: console 스코프 체인에서 검색(상위 스코프로 이동하며 전역 렉시컬 환경까지 이동) -> 객체 환경 레코드의 BindingObject를 통해 전역 객체에서 검색 가능

    2. log 메서드 검색: console 객체의 프로토타입 체인을 통해 메서드 검색

    3. 표현식 a+b+x+y+z의 평가: a,b,x,y,z 식별자를 스코프 체인에서 각각 검색
    4. console.log 메서드 호출: 평가된 값을 console.log 메서드에 전달하여 호출

 

8. bar 함수 코드 실행 종료

  - 실행 컨텍스트 스택에서 bar 함수 실행 컨텍스트 제거

  - 하지만 bar 함수의 렉시컬 환경까지 즉시 소멸되는 것은 아님! -> 누군가에 의해 참조되지 않을때 GC가 메모리에서 해제 시킴

  - 누군가 값을 참조하고 있다면 소멸되지 않음 (클로저와 연관된 내용)

 

9. foo 함수 코드 실행 종료

  - 실행 컨텍스트 스택에서 foo 함수 실행 컨텍스트 제거

 

10. 전역 코드 실행 종료

- 더는 실행 할 코드가 없으므로 전역 실행 컨텍스트도 스택에서 pop

 

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

let x = 1;

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

console.log(x); // 1

 

- var 키워드로 선언한 변수는 함수 레벨 스코프를 따른다.

- let, const로 선언한 변수는 블록레벨 스코프를 따른다. (if 문, for 문, while 문, try/catch 문 등)

- 블록 레벨 스코프가 새로 생성될때 외부 렉시컬 환경에 대한 참조는 블록 레벨 스코프가 생성되기 이전에 실행되고 있던 렉시컬 환경이다.

 

if문의 코드블록이 실행되어 새로운 렉시컬 환경 생성하고 기존의 렉시컬 환경을 교체
if문 코드 블록이 종료되면 이전 렉시컬 환경으로 복귀함