제육's 휘발성 코딩
article thumbnail
반응형

Execution Context 란?

Execution Context는 코드를 실행할 때 필요한 환경 정보들을 모아 둔 객체를 의미합니다. 자바스크립트에서는 실행 컨텍스트에 따라서 선언된 변수를 위로 끌어올리는 호이스팅, this 값 설정, 외부 환경 정보 구성 등의 동작이 발생합니다. 자바스크립가 실행 될 때마다 Execution Context 내에서 실행된다 정도로 이해하고 있습니다.

 

Execution Context 종류

자바스크립트에선 실행 컨텍스트를 다음과 같이 3가지 나누어서 분류되고 있습니다.

 

전역 실행 컨텍스트 (Global Execution Context)

전역 컨텍스트는 최상단의 공간으로 자바스크립트 파일이 실행되는 순간 전역 컨텍스트가 활성화됩니다. 전역 컨텍스트는 브라우저의 Window 객체인 전역 객체를 생성하고, this의 값을 전역 객체로 만들어줍니다. 전역 컨텍스트는 하나의 실행 컨텍스트만 갖고 있습니다.

 

함수 실행 컨텍스트 (Functional Execution Context)

함수가 호출할 때마다 새로운 실행 컨텍스트가 생성되며, 동시에 여러 컨텍스트가 존재할 수 있습니다. 생성된 실행 컨텍스트는 차례대로 수행됩니다.

 

Eval 실행 컨텍스트 (Eval Execution Context)

자바스크립트 코드인 eval() 함수를 사용하여 실행 컨텍스트를 생성하는 방식입니다. 보안 이슈로 인해 eval is evil (eval은 악마다)라고도 합니다.

 

Execution Context 생성 및 실행 과정

실행 컨텍스트는 생성, 실행 총 2 단계에 거쳐 만들어집니다. 생성된 실행 컨텍스트는 호이스팅, this 설정, 외부 환경 설정 등을 하는 역할을 하게 되는데 자바스크립트 엔진이 실행 컨텍스트를 생성 및 실행시키는 과정에 대해 알아봅시다.

실행 컨텍스트에는 크게 LexicalEnvironment, VariableEnvironment 두 가지 컴포넌트가 생성됩니다. 각 컴포넌트는 envioronmentRecordouterEnvironmentReference, ThisBinding 정보를 수집합니다.

 


envioronmentRecord는 함수와 변수 선언을 저장하는 곳입니다.

outerEnvironmentReference는 현재 컴포넌트 (Lexical, Variable)에서 변수를 찾지 못했다면 외부 환경에서 변수를 찾아본다는 의미입니다.

This binding은 this 값 할 당이 되는 시점입니다. 전연 컨텍스트는 브라우저에서 window, node에서 global 등이며, 함수 컨텍스트에선 this 호출 방식에 따라 다르게 됩니다.

 

1-1 LexicalEnvironment

LexicalEnvironment는 처음엔 VariableEnvironment과 같지만 변경 사항이 실시간으로 반영됩니다.

Lexical 환경에선 현재 컨텍스트와 관련된 코드의 식별자 정보들을 컨텍스트 내부 전체를 훑어가며 envioronmentRecord라는 컴포넌트에 저장합니다.

var a = 20;
var b = 40;

function foo() {
  console.log('bar');
}
  • 다음과 같은 코드가 있을 때
lexicalEnvironment = {
  a: 20,
  b: 40,
  foo: <ref. to foo function>
}
  • Lexical 환경에서는 다음과 같이 변환합니다.

 

1-2 VariableEnvironment

VariableEnvironment는 현재 컨텍스트 내의 식별자들에 대한 정보와 외부 환경 정보, 선언 시점의 LexicalEnvironment의 스냅샷으로 변경 사항은 반영되지 않습니다.

let a = 20;
const b = 30;
var c;

function multiply(e, f){
    var g = 20;
    return e * f * g;
}
c = multiply(20, 30);
  • 다음과 같은 코드가 있을 때
GlobalExectionContext = {
LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // Identifier bindings go here
      a: 20,
      b: 30,
      multiply: < func >
    }
    outer: <null>,
    ThisBinding: <Global Object>
  },
VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // Identifier bindings go here
      c: undefined,
    }
    outer: <null>,
    ThisBinding: <Global Object>
  }
}
  • 자바스크립트 엔진은 Global 실행 컨텍스트를 생성하고 하위에 LexicalEnvironment, VariableEnvironment 컴포넌트를 생성합니다.

 

호이스팅 

자바스크립트에서 호이스팅은 위에서 다룬 environmentRecord에서 저장된 식별자 정보를 토대로 실행됩니다. 

function a (x) {
    console.log(x); // 1

    var x;
    console.log(x); // 1

    var x = 2;
    console.log(x); // 2
}

a(1);
  • 코드를 살펴보면 1, undefined, 2 가 출력되야 할 것처럼 이해되는데 실제는 1, 1, 2가 출력되었습니다. 

 

environmentRecord의 관심사항은 어떤 식별자 (매개변수 식별자, 함수, 변수로 선언된 식별자)가 존재하는 지에만 관심이  있고, 어떤 값이 할당될 것인지에는 관심이 없습니다. 

 

function a (x) {
    var x; // 수집 대상 1의 변수 부분
    var x; // 수집 대상 2의 변수 부분
    var x; // 수집 대상 3의 변수 부분

    x = 1;	// 수집 대상 1의 할당 부분
    console.log(x);
    console.log(x);

    x = 2;	// 수집 대상 3의 할당 부분
    console.log(x);
}

a(1);
  • 다음과 같이 관심사항에 맞게 호이스팅 과정을 진행하고, 출력을 합니다. (변수는 선언부만 호이스팅을 거칩니다)

 

function a () {
    console.log(b); // b 함수 출력
    var b = 'bbb'; 
    console.log(b); // bbb
    function b() {} 
    console.log(b); // bbb
}

a();
  • 위의 코드를 실행하면 어떤 결과가 출력 될까요?  함수, bbb, bbb와 같이 출력됩니다. 관심 사항을 살펴보자면 변수 선언과 함수 선언이 있겠네요.

 

function a () {
    var b;              // 변수는 선언부만 호이스팅
    function b () {}    // 함수는 전체 호이스팅, var b = function b () {}

    console.log(b);    
    b = 'bbb';
    console.log(b);
    console.log(b);
}
  • 다음과 같이 변환되어 실행됩니다.  호이스팅이 끝난 상태에서의 함수 선언문은 함수명으로 선언한 변수에 함수를 할당한 것처럼 여길 수 있습니다.

 

console.log(sum(1,2));      // 3
console.log(multiply(3,4)); // multiply is not a function

function sum (a, b) {
    return a + b;
}

var multiply = function (a, b) {
    return a * b;
}
  • 함수는 전체가 호이스팅, 변수는 선언부만 호이스팅 되었기 때문에 위의 코드에선 sum() 함수만 정상 동작합니다. 

 

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


function sum (a, b) {
    return a + b;
}

function sum (a, b) {
    return a - b;
}
  • 호이스팅이 적용되면서 함수들이 위로 끌어올려질 때, 동일한 변수명에 서로 다른 값이 할당된 경우 마지막에 온 값이 이전 값을 덮어씌울 수 있습니다. 따라서 함수선언문이 아닌 변수에 함수를 담는 함수 표현식을 사용하는 것이 안전합니다.

 

REFERENCE

https://catsbi.oopy.io/fffa6930-ca30-4f7e-88b6-28011fde5867

https://blog.bitsrc.io/understanding-execution-context-and-execution-stack-in-javascript-1c9ea8642dd0

 

반응형
profile

제육's 휘발성 코딩

@sasca37

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요! 맞구독은 언제나 환영입니다^^