Javascript 실행 컨텍스트 (Execution Context)

Minho Yoo·2022년 8월 16일
1

실행 컨텍스트(Execution Context)란?

  1. 실행 가능한 코드가 실행되기 위해 필요한 환경
  2. 실행할 코드에 제공할 환경정보를 모아놓은 객체

실행 가능한 코드의 종류

  1. 전역 코드
  2. 함수 코드
  3. eval 코드 (eval은 사용하지 않아야 한다)

전역코드와 함수코드

일반적으로 실행이 가능한 코드는 전역코드와 함수코드이다.
자바스크립트 엔진은 코드를 실행하기 위해 여러가지 정보를 알고 있어야 한다.

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

위의 정보들을 물리적 객체의 형태로서 자바스크립트 엔진에서 관리한다.

Call stack

이벤트 루프를 언급할때 자바스크립트 엔진(javascript engine)에는 heap과 call stack이 존재한다.
heap은 메모리 할당이 일어나는 영역(변수, 객체 등등)
call stack은 코드의 실행에 따라 호출 스택이 쌓이는 영역이다.

함수가 호출되면 call stack에 실행 컨텍스트가 하나씩 쌓이게 된다.
코드의 실행과 함께 콜스택에 실행 컨텍스트가 쌓이는 모습을 보면 이렇다.

var a = 'a';
console.log(a);

위의 코드를 실행하면 아래와 같이 콜스택에 올려져 실행된다.
전역 범위에서 실행되었으므로 전역 실행컨텍스트 하나만 스택에 존재하는 것을 확인할 수 있다.

이제 좀 더 복잡한 코드의 실행이다.

var a = 'a';

function outerFunc() {
  var b = 'b';
  
  function innerFunc() {
    console.log(a + b);
  }
  
  innerFunc();
}

outerFunc();
  1. 전역 코드 실행 (global EC 추가)
  2. outerFunc 함수 실행 (outerFunc EC 추가)
  3. innerFunc 함수 실행 (innerFunc EC 추가)

총 3개의 스택이 추가되는 것을 볼 수 있고 실행 후에는 해당 함수의 실행 컨텍스트를 파기하고 컨트롤을 다음 스택으로 반환하게 된다.

실행 컨텍스트의 구조

실행컨텍스트는 실행 가능한 코드를 형상화하고 구분하는 추상적인 개념이지만 물리적으로는 객체의 형태를 가지며 Variable Object, Scope Chain, thisValue 이 3가지의 프로퍼티를 가지고 있다.

Variable Object

위에서 언급한 여러가지 정보중 변수, 매개변수, 함수선언에 대한 정보가 담길 객체이다.

  • 변수
  • 매개변수, 인수정보
  • 함수 선언

Variable Object는 실행 컨텍스트의 property이기 때문에 상황에 따라 다른 값을 가지게 된다.
하나의 예로 전역 컨텍스트에는 매개변수가 없지만 함수 컨텍스트에는 매개변수가 있다.

전역 컨텍스트

함수 컨텍스트

Scope Chain

프로그래밍에는 동적 스코프, 정적 스코프(lexical scope)가 존재합니다.

동적 스코프

함수가 호출될 때 스코프가 정해진다.

정적 스코프

함수가 선언된 위치를 기준으로 스코프가 정해진다.

javascript에서는 정적 스코프를 체택하고 있다. (대부분의 언어에서는 정적 스코프를 체택하고 있다)
선언된 위치를 기준으로 스코프를 가지게 된다는 것이다.
var는 함수 스코프, let과 const는 블록 스코프를 가지는데 이것 또한 정적 스코프이다.

실행컨텍스트에서는 Scope Chain은 자신이 선언된 위치를 기준으로 생성 된다.
innerFunc은 outerFunc 내부에서 선언되었다.
outerFunc는 전역 스코프에서 선언되었다.
그러므로 innerFunc의 스코프 체인 [innerFunc, outerFunc, 전역컨텍스트]의 값을 가지게 된다.
이 스코프 체인은 변수의 값을 탐색할 때 사용된다.

// 전역 컨텍스트
context: {
  vo: {
	  a: 'a',
      outerFunc: function Ojbect
  },
  sc: ['전역 변수객체'],
  this: window
}

outerFunc() {
  vo: {
  	b: 'b',
    innerfunc: function Object
    arguments: {}
  },
  sc: ['outerFunc 변수객체', '전역 변수객체'],
  this: window
}

innerFunc: {
  vo: {
  	arguments: {}
  },
  sc: ['innerFunc 변수객체', 'outerFunc 변수객체', '전역 변수객체'],
  this: window
}

크롬의 개발자 도구에서는 Scope, CallStack을 확인할 수 있다.
중간에 debugger를 찍으면 아래의 사진과 같이 확인할 수 있다.

함수의 감춰진 프로퍼티 중 [[scope]]는 스코프 체인이 아니라 현재 참조하고 있는 스코프를 의미한다.

thisValue

this는 동적으로 결정된다.
이는 예측이 여러운 상황을 종종 만들어내고, this는 함수와 다르게 선언되는 위치가 아니라 호출되는 상황에 따라 동적으로 값이 할당된다.

this가 결정되는 상황은 크게 5가지 상황이 있다.

  1. 일반 함수로 실행
  2. 메소드로 실행
  3. 생성자 함수
  4. bind, apply, call 을 사용한 명시적 바인딩
  5. 콜백함수에서의 실행
profile
Always happy coding 😊

0개의 댓글