코어자바스크립트 | 실행컨텍스트

실행 컨텍스트

스택 & 큐

스택(Stack): FILO 출입구가 하나, 비어있는 스택에 순서대로 1,2,3,4 로 저장하면 꺼낼 때는 4,3,2,1 로 꺼내야 한다.
큐(Queue): FIFO 양쪽이 모두 열려있는 파이프, 종류에 따라 양쪽 모두 입력과 출력이 가능한 큐도 있지만, 보통 한쪽은 입력만 다른 한쪽은 출력만을 담당.



실행 컨텍스트란?

  • 실행할 때 필요한 환경 정보들을 모아놓은 객체
  • 동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고,
    이를 콜 스택에 쌓아올렸다가, 가장 위에 쌀여있는 컨텍스트와 관련 있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장한다.

여기서 "동일한 환경" , 즉 하나의 실행 컨텍스트를 구성할 수 있는 방법으로

  • 전역공간
  • eval() 함수
  • 함수 등.

우리가 흔히 실행 컨텍스트를 구성하는 방법은 함수를 실행 하는 것 뿐. ( ES6 에서는 "블록{}" 에 의해서도 새로운 실행컨텍스트가 생성된다. )

[에제1번] 실행컨텍스트와 콜 스택.

// ------------------------------------- (1)

/* (1) 자바스크립트 코드 실행하는 순간.
 * 전역 컨텍스트가 콜 스택에 담긴다.
 * 전역 컨텍스트: 일반적인 실행 컨텍스트와 특별히 다를 것이 없음.
 * 최상단의 공간은 코드 내부에서 별도의 실행 명령이 없어도 브라우저에서 자동으로 실행하므로,
 * 자바스크립트 파일이 열리는 순간 전역 컨텍스트가 활성화된다.
 */

var a = 1;

function outer() {
  function inner() {
    console.log(a); // undefined
    var a = 3;
  }

  inner(); // -----------------(2)
  /* inner 함수가 다시 콜스택의 최상단에 들어가게 된다.
   * outer 멈추고 inner 함수 내부의 코드 순차적으로 실행.
   */
  console.log(a);
}

outer(); // ------------------(3)
/* outer함수를 호출하면 outer 에 대한 환경 정보를 수집해 실행컨텍스트를 생성 후 콜스택에 담는다.
 * 콜스택의 맨 위에 outer 실행컨텍스트가 놓인 상태 전역 컨텍스트 멈추고 outer 부터 코드 실행.
 */
console.log(a);

실행 컨택스트가 콜 스택의 맨 위에 쌓이는 순간이 현재 실행할 코드에 관여하게 되는 시점. => 런타임
어떤 실행컨텍스트가 활성화 될 때, 자바스크립트 엔진은 해당 컨텍스트에 관련된 코드들을 실행하는 데 필요한 환경 정보들을 수집해 실행 컨텍스트 객체에 저장.
🧐 "This" Binding 의 시점

📦 실행컨텍스트 객체의 수집 정보

  • VariableEnvironment : 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보. 선언 시점의 LexicalEnvironment 의 스냅샷으로 변경사항은 반영되지 않는다.
  • LexicalEnvironment : 처음에는 VariableEnvironment 와 같지만 변경사항이 실시간으로 반영됨.
  • ThisBinding : this 식별자가 바라봐야 할 대상 객체.



이미지 출처

VariableEnvironment

VariableEnvironment 에 담기는 내용은 LexicalEnvironment 와 같지만 최초 실행 시의 스냅샷을 유지한다는 점이 다름.

  • 실행컨텍스트를 생성할 때 VariableEnvironment에 먼저 정보를 담고 그대로 복사해 LexicalEnvironment 를 만들고, 이후에는 LexicalEnvironment를 주로 활용.
  • VariableEnvironment와 LexicalEnvironment의 내부는 environmentRecord와 outer-EnvironmentReference로 구성되어 있다.
    초기화 과정에서는 완전히 동일.

LexicalEnvironment

어휘적 환경.
실행 컨텍스트를 구성하는 환경 정보들을 모아놓은 것.

environmentRecord

environmentRecord 에는 현재 컨텍스트와 관련된 코드의 식별자 정보 들이 순서대로 저장.

  1. 매개변수 식별자
  2. 선언한 함수 자체
  3. var로 선언된 변수명.

위에서 변수 정보를 수집하는 과정을 모두 마쳤다해도, 실행 컨텍스트가 관여하는 코드들은 아직 실행 되기 전의 상태임.

코드가 실행되기 전이지만 자바스크립트 엔진은 이미 해당 환경에 속한 코드의 변수명을 이미 알고 있다.
즉, 자바스크립트 엔진은 식별자들을 최상단으로 끌어올림.(호이스팅)

호이스팅

environmentRecord 에는 현재 실행될 컨텍스트의 대상 코드 내에 어떤 식별자들이 있는지만 관심이 있고, 각 식별자에 어떤 값이 할당될 것인지는 관심이 없다.
변수를 호이스팅 할 때, 변수명만 끌어올리고 할당 과정은 원래 자리에 남겨둠.

함수 선언문과 함수 표현식


  1. 원본 코드
console.log(sum(1, 2));
console.log(multiply(3, 4));

// 함수 선언문
function sum(a, b) {
  return a + b;
}

// 함수 표현식
var multiply = function (a, b) {
  return a * b;
};
  1. 호이스팅 마친 후
// 함수 선언문은 함수 전체를 호이스팅.
var sum = function (a, b) {
  return a + b;
};

// 함수 표현식은 변수의 선언부로 호이스팅.
var multiply;

console.log(sum(1, 2));

//Uncaught TypeError: multiply is not a function at <anonymous>:9:13
console.log(multiply(3, 4));

// 변수의 할당부는 원래 자리에 남김.
multiply = function (a, b) {
  return a * b;
};

-> 함수 선언문은 선언 전에 호출해도 실행된다. 함수 표현식은 선언 전에 호출하면 Error 발생.

함수 선언문은 함수 전체를 호이스팅, 함수 표현식은 변수 선언부만 호이스팅하고 할당부는 원래 자리에 남겨둠.
전역 컨텍스트가 활성화될 때, 전역공간에 선언된 함수들이 모두 가장 위로 끌어올려짐.
동일한 변수명에 서로 다른 값을 할당할 경우 override 발생.
함수선언문보다 함수표현식 사용하는 것을 추천.

스코프

  • 식별자에 대한 유효범위.
  • ES5 까지의 자바스크립트는 전역공간을 제외하면 오직 함수에 의해서만 스코프가 생성.
  • ES6 에서는 블록에 의해서도 스코프가 생성. var는 함수스코프. let, const, class, strict mode에서의 함수 선언 등에 대해서만 블록 스코프.

스코프 체인

  • 식별자의 유효범위를 안에서부터 바깥으로 차례로 검색해 나가는 것.
    이를 가능하게 만드는 것이 LexicalEnvironment 의 두번째 수집자료인 outerEnvironmentReference.

outerEnvironmentReference

  • 현재 호출된 함수가 선언될 당시의 LexicalEnvironment를 참조한다.
  • A 함수 내부에 B 함수를 선언하고 다시 B 함수 내부에 C 함수를 선언한 경우, 함수 C의 outerEnvironmentReference는 함수 B의 LexicalEnvironment 를 참조.
    함수 B의 LexicalEnvironment에 있는 outerEnvironmentReference는 연결리스트 형태를 띄고, 선언 시점의 LexicalEnvironment만 참조하기 때문에 가장 가까운 요소부터 차례대로 접근하고 다른 순서로 접근하는 것은 불가능. -> 여러 스코프에서 동일한 식별자를 선언한 경우, 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근 가능.
var a = 1;

var outer = function () {
  var inner = function () {
    console.log(a);
    var a = 3;
  };
  inner();
  console.log(a);
};

outer();
console.log(a);
// 위의 코드를 실행 순서대로 풀어서 쓴다면,,,?

var a; // 1번
var outer; // 2번

outer = function () {
  // 4번
  var inner; // 5번
  inner = function () {
    // 7번
    var a; // 8번
    console.log(a);
    // 9번  스코프체인에서 첫번째 인자 a에 접근하게 되기 때문에 undefined. 변수 은닉화
    a = 3; // 10번
  };
  inner(); // 6번
  console.log(a); // 11번
};

outer(); //3번
console.log(a); // 12번

변수의 은닉화

스코프 체인 상에 있는 변수라고 해서 무조건 접근 가능한 것은 아니다.
식별자 a 는 전역 공간에서도 선언했고, inner 함수 내부에서도 선언.
inner 함수 내부에서 a에 접근하려고 하면 무조건 스코프 체인 상의 첫 번째 인자,
즉 inner 스코프의 LexicalEnvironment 부터 검색할 수 밖에 없다. inner 스코프의 LexicalEnvironment에 a 식별자가 존재하므로 스코프 체인 검색을 더 진행하지 않고 즉시 inner Environment 상의 a를 반환한다.
-> inner 함수 내부에서 a 변수를 선언했기 때문에 전역 공간에서 선언한 동일한 이름의 a 변수에는 접근할 수 없음 (변수의 은닉화)

전역변수와 지역변수

  • 전역 공간에서 선언한 변수는 전역 변수.
  • 함수 내부에서 선언한 변수는 지역변수.

This

  • 실행 컨텍스트의 thisBinding 에는 this로 지정된 객체가 저장.
  • 실행 컨텍스트 활성화 당시에 this가 지정되지 않는다면, 전역 객체 (Node -> Global / 브라우저 -> window)가 저장된다.
  • 그 밖에는 함수를 호출하는 방법에 따라 this 에 저장되는 대상이 다르다.

요약

실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체.
실행 컨텍스트는 전역 컨텍스트, eval 및 함수 실행에 의한 컨텍스트가 있다.
실행 컨텍스트 객체는 활성화되는 시점에 VariableEnvironment, LexicalEnvironment, thisBinding의 세가지 정보를 수집.

좋은 웹페이지 즐겨찾기