[JavaScript] 실행 컨텍스트

Letmegooutside·2022년 1월 15일
0

JavaScript

목록 보기
8/25
post-thumbnail

실행 컨텍스트

실행 가능한 코드가 실행되기 위해 필요한 환경

실행 가능한 코드

  • 전역 코드 : 전역 영역에 존재하는 코드
  • Eval 코드 : eval 함수로 실행되는 코드
  • 함수 코드 : 함수 내에 존재하는 코드

(일반적으로 실행 가능한 코드는 전역 코드와 함수 코드이다.)

자바스크립트 엔진은 코드를 실행하기 위해 실행에 필요한 여러가지 정보를 알고 있어야 한다.

  • 변수 : 전역변수, 지역변수, 매개변수, 객체의 프로퍼티
  • 함수 선언
  • 변수의 유효범위(Scope)
  • this

이와 같이 실행에 필요한 정보를 형상화하고 구분하기 위해서 자바스크립트 엔진은 실행 컨텍스트를 물리적 객체의 형태로 관리한다.

var x = 'xxx';

function foo () {
  var y = 'yyy';

  function bar () {
    var z = 'zzz';
    console.log(x + y + z);
  }
  bar();
}
foo();

위 코드를 실행하면 아래와 같이 실행 컨텍스트 스택이 생성되고 소멸된다.
현재 실행 중인 컨텍스트에서 현재 컨텍스트와 관련 없는 코드(예:다른 함수)가 실행되면 새로운 컨텍스트가 생성되어 스택에 쌓이고 컨트롤(제어권)이 이동한다.

  • 전역 실행 컨텍스트는 애플리케이션이 종료될 때 까지 유지된다.
  • 함수를 호출하면 해당 함수의 실행 컨텍스트가 생성되어 직전에 실행된 코드 블록의 실행 컨텍스트 위에 쌓인다.
  • 함수 실행이 끝나면 함수의 실행 컨텍스트를 파기하고 직전 실행 컨텍스트에 컨트롤을 반환한다.

실행 컨텍스트의 3가지 객체

실행 컨텍스트는 물리적으로 객체의 형태를 가지며 아래의 3가지 프로퍼티를 소유한다.

1. Variable Object(VO/변수 객체)

실행에 필요한 여러 정보들을 담는 객체

코드가 실행될 때 엔진에 의해 참조되며 코드에서는 접근할 수 없다.

변수 객체는 아래의 정보를 담는 객체이다.

  • 변수
  • 매개변수와 인수 정보
  • 함수 선언 (함수 표현식은 제외)

전역 코드와 함수의 내용은 다르기 때문에 전역 컨텍스트의 경우와 함수 컨텍스트의 경우에서 가리키는 객체가 다르다.

전역 컨텍스트

Global Object(GO/전역 객체)를 가리키며 전역에 선언된 전역 변수와 전역 함수를 프로퍼티로 소유한다.

함수 컨텍스트

Active Object(AO/활성 객체)를 가리키며 매개변수와 인수들의 정보를 배열의 형태로 담고 있는 객체인 arguments object가 추가된다.

함수가 호출되면 함수 컨텍스트가 생성되고, 함수 컨텍스트에는 AO라는 특수 객체가 생성되는데 AO는 함수 컨텍스트의 VO로 사용된다.

활성 객체 : 자바스크립트 엔진이 해당 컨텍스트에서 실행에 필요한 여러 가지 정보를 담을 목적으로 생성하는 객체

2.Scope Chain(SC)

해당 코드의 유효 범위 안에 있는 변수를 정의하는 객체의 리스트

해당 전역 또는 함수가 참조할 수 있는 변수, 함수 선언 등의 정보를 담고 있는 전역 객체 또는 활성 객체의 리스트를 가리킨다.

현재 실행 컨텍스트의 활성 객체를 선두로 하여 순차적으로 상위 컨텍스트의 활성 객체를 가리키며 마지막 리스트는 전역 객체를 가리킨다.

스코프 체인은 함수의 내부 프로퍼티인 [[Scope]]로 참조할 수 있다.

함수 내의 코드에서 변수를 참조하면 엔진은 스코프 체인의 첫번째 리스트가 가리키는 AO에 접근하여 변수를 검색한다.
만일 검색에 실패하면 다음 리스트가 가리키는 AO 혹은 GO를 검색한다. 이와 같이 순차적으로 스코프 체인에서 변수를 검색하는데 검색에 실패하면 정의되지 않은 변수에 접근하는 것으로 판단하여 Reference 에러를 발생시킨다.(아래와 같은 느낌)

3.this value

this 값이 할당된다.

this에 할당되는 값은 함수 호출 패턴에 의해 결정된다.

실행 컨텍스트의 생성 과정

아래의 코드를 가지고 어떻게 실행 컨텍스트가 생성되는지 알아보자

var x = 'xxx';

function foo () {
  var y = 'yyy';

  function bar () {
    var z = 'zzz';
    console.log(x + y + z);
  }
  bar();
}

foo();

1. 전역 코드에 진입

컨트롤이 실행 컨텍스트에 진입하기 전에 유일한 전역 객체가 생성된다.
이 객체의 프로퍼티는 코드 어떤 곳에서도 접근할 수 있다.

전역 코드로 컨트롤이 진입하면 전역 실행 컨텍스트가 생성되고 실행 컨텍스트 스택에 쌓인다.

이후 이 실행 컨텍스트를 바탕으로 아래의 처리가 실행된다.

  1. 스코프 체인의 생성과 초기화
  2. Variable Instantiation(변수 객체화) 실행
  3. this value 결정

1-1. 스코프 체인의 생성과 초기화

실행 컨텍스트가 생성된 이후 가장 먼저 스코프 체인의 생성과 초기화가 실행된다.

1-2. Variable Instantiation(변수 객체화) 실행

Variable Object에 프로퍼티와 값을 추가하는 것을 의미한다.
전역 코드의 경우, VO는 GO를 가리킨다.

변수 객체화는 아래의 순서로 프로퍼티와 값을 설정한다.

  1. (함수인 경우) 매개변수가 VO의 프로퍼티로, 인수가 값으로 설정된다.
  2. 대상 코드 내의 함수 선언(함수 표현식 제외)을 대상으로 함수명이 VO의 프로퍼티로, 생성된 함수 객체가 값으로 설정된다. (함수 호이스팅)
  3. 대상 코드 내의 변수 선언을 대상으로 변수명이 VO의 프로퍼티로, undefined가 값으로 설정된다. (변수 호이스팅)

함수의 선언처리
선언된 함수명이 VO의 프로퍼티로, 생성된 함수 객체가 값으로 설정된다.

생성된 함수 객체는 [[Scopes]]프로퍼티를 가지게된다.
[[Scopes]]프로퍼티는 함수 객체만이 소유하는 내부 프로퍼티로서 함수 객체가 실행되는 환경을 가리킨다.
따라서 현재 실행 컨텍스트 스코프 체인이 참조하고 있는 객체를 값으로 설정한다.

내부 함수의 [[Scopes]]프로퍼티는 자신의 실행 환경(Lexical Environment)과 자신을 포함하는 외부 함수의 실행 환경과 전역 객체를 가리키게 되는데, 이때 자신을 포함하는 외부 함수의 실행 컨텍스트가 소멸하여도 [[Scopes]]프로퍼티가 가리키는 외부 함수의 실행 환경은 소멸하지 않고 참조할 수 있다.
이것이 클로저이다.

+ 변수의 선언처리, 호이스팅

1-3. this.value 결정

변수 선언 처리가 끝나면 this value가 결정된다.
this value가 결정되기 이전에 this는 전역 객체를 가리키고 있다가, 함수 호출 패턴에 의해 this에 할당되는 값이 결정된다.

전역 컨텍스트의 경우, VO, 스코프 체인, this는 언제나 전역 객체를 가리킨다.

2. 전역 코드의 실행

var x = 'xxx';

function foo () {
  var y = 'yyy';

  function bar () {
    var z = 'zzz';
    console.log(x + y + z);
  }
  bar();
}

foo();

위의 예제에서 전역 코드는 전역변수 x에 문자열 'xxx'를 할당하는 것과 함수 foo의 호출이 있다.

2-1. 변수 값의 할당

전역 변수 x에 문자열 'xxx'를 할당할 때, 현재 실행 컨텍스트의 스코프 체인이 참조하고 있는 VO를 선두부터 검색하면서 변수명에 해당하는 프로퍼티가 발견되면 값('xxx')을 할당한다.

2-1. 함수 foo의 실행

전역 코드의 함수 foo가 실행되기 시작하면, 새로운 함수 실행 컨텍스트가 생성된다.
함수 foo의 실행 컨텍스트로 컨트롤이 이동하면 전역 코드의 경우와 마찬가지로 1. 스코프 체인의 생성과 초기화, 2. Variable Instantiation 실행, 3. this value 결정이 순차적으로 실행되는데, 이 때 전역 코드의 룰이 아닌 함수 코드의 룰이 적용 된다.

스코프 체인의 생성과 초기화

함수 코드에서는 우선 AO에 대한 레퍼런스를 스코프 체인의 선두에 설정하는 것으로 시작된다.

AO는 우선 arguments 프로퍼티의 초기화를 실행하고, 그 후 변수 객체화가 실행된다.

그 후 Caller(여기서는 전역 컨텍스트)의 스코프 체인이 참조하고 있는 객체가 스코프 체인에 push된다.
따라서 이 경우 함수 foo 를 실행 직후 실행 컨텍스트의 스코프 체인은 AO와 전역 객체를 순차적으로 참조한다.

변수 객체화 실행

스코프 체인의 생성과 초기화에서 생성된 AO를 VO로서 변수 객체화가 실행된다.
이것을 제외하면 전역 코드의 경우와 같은 처리가 실행된다.

this value의 결정

변수 선언 처리가 끝나면 this value가 결정된다.

3. foo 함수 코드의 실행

이제 foo의 코드 블록 내 구문이 실행된다.
변수 y에 문자열 'yyy'의 할당과 함수 bar가 실행된다.

3-1. 변수 값의 할당

지역 변수 y에 문자열 'yyy'를 할당할 때, 현재 실행 컨텍스트의 스코프 체인이 참조하고 있는 VO를 선두에서부터 검색하여 변수명에 해당하는 프로퍼티가 발견되면 값 'yyy'를 할당한다.

3-3. 함수 bar의 실행

함수 bar가 실행되기 시작하면 새로운 실행 컨텍스트가 생성되고, 이전 foo의 실행과정과 동일하게 1. 스코프 체인의 생성과 초기화, 2. Variable Instantiation 실행, 3. this value 결정이 순차적으로 실행된다.

이 단계에서 console.log(x + y + z); 구문의 실행 결과는 xxxyyyzzz가 된다.

  • x : AO-2에서 x 검색 실패 → AO-1에서 x 검색 실패 → GO에서 x 검색 성공 (값은 ‘xxx’)
  • y : AO-2에서 y 검색 실패 → AO-1에서 y 검색 성공 (값은 ‘yyy’)
  • z : AO-2에서 z 검색 성공 (값은 ‘zzz’)

요약

다른 함수나 현재 실행 컨텍스트로 실행하지 못하는 코드를 만났을 경우,

  1. 실행 컨텍스트 생성
    1-1. 스코프 체인 생성과 초기화
    1-2. 변수객체화
    1-3. this value결정
  2. 코드 실행

위의 과정이 이루어진다.




Refernece
https://poiemaweb.com/js-execution-context 를 정리한 내용입니다.

0개의 댓글