Pandaman Blog

[JS] Javascript의 실행 컨텍스트 (Execution Context) 본문

Front end/Javascript

[JS] Javascript의 실행 컨텍스트 (Execution Context)

oyg0420 2020. 2. 6. 22:50

Javascript 실행컨택스트 (Execution Context)

코드가 평가되고 실행되는 환경의 추상적인 개념입니다. 즉 실행 컨텍스트는 실행 가능한 코드가 실행되기 위해 필요한 환경이라고 할 수 있습니다.

지금부터 실행 컨텍스트의 종류에 대해서 알아보겠습니다.

global execution context

코드에 진입하면 생성되는 global execution context는 가장 기본이되는 execution context 입니다.

functional execution context

함수가 호출될 때마다 호출된 함수의 새로운 실행 컨택스트를 생성합니다. 각 함수는 자신만의 실행 컨텍스트를 가지고 있습니다. 따라서, 이 함수의 실행 컨텍스트는 얼마든지 존재할 수 있습니다.가 아니라 브라우저의 실행스택의 용량에 따라 제한되어 생성됩니다.
실행 컨텍스트는 생성될 때마다 정의된 순서대로 진행합니다.

실행 스택

실행 스택은 LIFO(Last in First Out)의 구조를 갖고 있습니다. 실행 스택은 코드 실행되는 동안 생성된 모든 실행 컨텍스트를 저장하는 데 사용합니다.

javascript 엔진이 스크립트를 발견하면 global execution context를 생성하고 이것을 execution stack에 푸시합니다. 함수가 호출을 될 때마다, 호출된 함수의 새로운 execution context를 생성하고 execution stack에 푸시합니다.

자바스크립트 엔진은 execution stack의 맨 위에 있는 function execution context에 해당하는 함수를 실행합니다. 그리고 이 함수가 완료되면 해당 execution contextexecution stack에서 튀어나오게 되어, 현재의 stack에서의 제어권(control)이 바로 아래 있는 execution context로 가게 됩니다.

아래의 예제를 통해 알아보겠습니다.

let a = 'Hello Pandaman World!';
function first() {
  alert('Inside first function');
  second();
  alert('Again inside first function');
}
function second() {
  alert('Inside second function');
}
first();
  1. 브라우저에서 위의 코드가 로드되었을 때 자바스크립트 엔진은 전역 실행 컨텍스트를 생성합니다. 그리고 생성된 전역 실행 컨텍스트를 실행 스택에 푸시합니다.
  2. first() 함수가 호출되면 자바스크립트 엔진은 해당 함수에 대한 실행 컨텍스트를 현재 실행 스택 위에 푸시합니다.
  3. first() 함수 내부의 second() 함수가 호출되면 해당 함수에 대한 실행 컨텍스트를 현재 실행 스택의 맨 위로 푸시합니다.
  4. second() 함수가 기능을 완료하면 이 함수의 실행 컨텍스트는 실행 스택으로부터 나가게 됩니다.
  5. first() 함수가 기능을 완료하면 이 함수의 실행 컨텍스트는 실행 스택으로부터 나가게 되고 제어권은 전역 실행 컨텍스트로 가게 됩니다.
  6. 모든 코드가 실행되면 자바스크립트 엔진은 현재 스택으로부터 전역 실행 컨텍스트를 제거하게 됩니다.
    지금까지 자바스크립트 엔진이 실행 컨텍스트를 어떻게 관리하는지 확인해보았습니다. 결론은 실행컨텍스트가 생성되며 실행스택에 Push 하고, 실행이 완료되면 실행스택에서 컨텍스트는 Pop를 합니다.

지금부터는 ES5.1 전후로 나누어 실행컨텍스의 동작방식에 대해 설명해보겠습니다.

ES5.1 이전

실행컨텍스트는 실행 가능한 코드를 형상화하고 구분하는 추상적인 개념이지만 물리적으로는 객체의 형태를 가지며, 아래의 속성을 갖습니다.

1) Variable Object(변수 객체)
2) Scope chain(스코프 체인)
3) this value

Variable Object (변수 객체)

Variable Object는 실행컨텍스트와 관련 있는 데이터 컨테이너입니다. 데이터 컨테이너라고 하는 이유는 변수, 함수 선언, 인수 객체를 저장하기 때문입니다. Variable Object는 추상적인 개념입니다. 각각의 컨텍스트에서 다른 객체를 사용하여 나타냅니다.
각각의 컨텍스트 위에서 말한 전역 실행 컨텍스트와 함수 실행 컨텍스트입니다.

1) 전역 실행 컨텍스트에서의 Variable Object는 전역 객체(Global Object)입니다.
아래 예제는 전역에 선언된 변수와 함수 선언을 전역(window) 객체에 저장하고, 다시 불러오는 예제입니다.

var x = 'Global variable';

function fn1() {
  console.log('global function')
};

console.log(window.fn1 === fn1) // true
console.log(window.x === x) // true

위에서 선언한 변수 x는 Global VO(Variable Object) 저장됩니다. 따라서 전역 객체인 window 객체에서 변수 x에 접근이 가능합니다. 그리고 함수 fn1도 동일하게 Global VO에 저장되어 전역 객체의 속성 이름을 통해 fn1에 접근할 수 있었고, 전역 객체에서 접근한 fn1함수와 전역에서 선언한 fn1함수는 동일하다는 것을 확인할 수 있었습니다.

위의 코드를 Global VO(variable Object)로 나타내 보겠습니다.

GlobalVO = {
 x: 'Global variable',
 fn1: <function>
}

2) 함수가 실행되면 함수 실행 컨텍스트가 생성됩니다. 그리고 함수 실행 컨텍스트에서는 Activation Object(활성 객체)가 생성됩니다. 현재 control 되고 있는 함수 실행 컨텍스트의 변수 객체(Variable Object)는 활성되고 있는 객체를 의미하고, 활성 객체(Activation Object)라고 부릅니다.

활성 객체는 매개변수와 인수 객체(arguments)가 포함되어 있습니다. 그리고 Activation Object(활성 객체)는 함수 실행 컨텍스트에서 Variable Object로 사용됩니다.

function fn1(x, y) {
  var z = 'Local variable';
  function fn2() {
  console.log('this is fn2 function');
  }
  fn2();
};

fn1(5, 3);

위의 코드 중 함수 fn1이 호출될 때 생성된 함수 실행 컨텍스트의 Activation Object를 객체 형태로 표현해보겠습니다.

arguments 객체는 배열의 형태의 객체입니다. 인덱스와 인수의 길이(개수)를 포함하고 있습니다.

매개변수 x, y, 지역변수 z, 함수 fn2가 ActivationObject의 속성으로 저장되었습니다.

ActivationObject = {
 arguments: {0: 5, 1: 3, length: 2},
 x: 5,
 y: 3,
 z: 'Local variable',
 fn2: <function>,
}

Scope chain

스코프 체인은 일종의 리스트로서 실행 컨텍스트에 존재하는 식별자를 찾기 위한 객체 리스트입니다. 여기서 말하는 식별자는 변수의 이름, 함수 선언, 매개변수 등이 있습니다. 스코프 체인은 자신의 상위 변수 객체의 리스트입니다. 0번째 인덱스에는 자신의 변수 객체가 포함되어 있습니다.
함수 실행 중에 변수를 만나면 현재 실행컨텍스트의 스코프 체인의 0번째 Activation Object에서 찾아보고, 없다면 스코프 체인에 담긴 순서대로 Variable Object를 검색을 하게 됩니다.

간단한 체인 스코프 예제를 보시겠습니다.

var name = 'global variable';

function sayHo() {
  var name = 'sayHo variable';
  function sayYo() {
    alert(name);
  };
  sayYo();
};

sayHo();

위의 코드를 간단하게 변수 객체와 체인 코드로 표현해보겠습니다.

GlobalVO = {
 name: 'global variable',
 sayHo: <function>,
}

SayHoAO = {
 arguments: null,
 sayYo: <function>,
 name: 'sayHo variable'
}

SayYoAO = {
 arguments: null,
}

SayYoScopeChain = [SayYoAO, SayHoAO, GlobalVO]

SayYo함수에서 어떤 name을 참조할 것인가가 이 코드에서 알아볼 내용입니다. 일단 자신의 AO에서 name을 찾으면 없습니다. 그럼 체인 코드에서 1번 인덱스의 SayHoAO 객체에서 name을 찾아보겠습니다. 마침 바로 있습니다. 따라서, name은 'sayHo variable'이 되겠습니다.

지난번 글에서 배운 lexical scope도 여기서 적용해보겠습니다.

var name = 'global variable';

function sayYo() {
    alert(name);
};

function sayHo() {
  var name = 'sayHo variable';
  sayYo();
};

sayHo();

lexical scope를 공부하셨다면 sayYo 함수의 변수 name이 무엇을 참조할지 알 수 있습니다. 바로 'global variable'를 참조하겠습니다. 그 이유는 함수가 선언될 때 둘러싼 스코프에서 먼저 변수를 찾아보기 때문이죠, 이것을 체인 스코프에 적용해보겠습니다.

GlobalVO = {
 name: 'global variable',
 sayYo: <function>,
 sayHo: <function>,
}

SayHoAO = {
 arguments: null,
 sayYo: <function>,
 name: 'sayHo variable'
}

SayYoAO = {
 arguments: null,
}

SayYoScopeChain = [SayYoAO, GlobalVO];

SayYo함수의 스코프 체인을 확인해보면 [SayYoAO, GlobalVO]가 되는 것을 확인할 수 있습니다. SayYo함수를 감싸고 있는 실행 컨텍스트는 무엇일까요? 바로 전역 컨텍스트입니다. 따라서 SayYo에서 name을 찾을 때 자신을 감싸고 있는 실행컨텍스트의 VO(Variable Object)을 참조하여 식별자를 검색하게 됩니다. 그리고 이러한 메커니즘을 Lexical Scope라고 부릅니다.
SayHo함수에서는 단지 호출만 되었을 뿐이지 SayYo 실행컨텍스트와 SayHo함수는 관련이 없는 것을 알 수 있습니다. 따라서 자신의 상위 실행컨텍스트의 변수 객체는 GlobalVO가 되어 name'global variable'를 참조하게 됩니다.

this value

this는 값은 실행 컨텍스트와 관련된 특별한 객체입니다. 어떤 객체든 컨텍스트에서 this의 값으로 사용될 수 있습니다.
전역 컨텍스트에서의 this값은 전역 객체입니다. 함수 컨텍스트에서의 this는 어떠한 방식으로 호출하냐에 따라서 this가 달라집니다.

실행컨텍스트는 두 가지 단계로 나눌 수 있습니다. 생성 단계와 실행단계입니다.

실행 컨텍스트의 생성과정

생성 단계에서는 아래와 같은 순서로 컨텍스트가 생성이 됩니다.

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

  2. Variable Instantiation(변수 객체화) 실행
    스코프 체인의 생성과 초기화가 종료하면 Variable Instantiation가 실행되는데, Variable InstantiationVariable Object에 속성과 값을 추가하는 것을 의미합니다.
    1) (Function Code인 경우) 매개변수(parameter)가 Variable Object의 속성의 이름으로, arguments가 값으로 설정.
    2) 대상 코드 내의 함수 선언을 대상으로 함수명이 Variable Object의 속성의 이름으로, 생성된 함수 객체가 값으로 설정
    3) 대상 코드 내의 변수 선언을 대상으로 변수명이 Variable Object의 속성의 이름으로, undefined가 값으로 설정

참고로 동일 컨텍스트에서 위 순서는 상관없이 동일한 이름을 가진 함수와 변수의 선언이 있다면 변수 선언을 한 변수의 값이 함수의 선언을 한 함수 객체를 위에 덮어 씌우게 됩니다.

var same = 'test';
function same(){
}
same // 'test'
function same(){
}
var same = 'test';
same // 'test'
  1. this value 결정
    변수의 선언 처리가 끝나면 다음은 this 값이 결정됩니다. this의 값이 결정되기 이전에는 전역 객체를 가리키고 있다가, 함수 호출 패턴에 의해서 this에 값이 결정됩니다.

아래 예제를 통해서 실행 컨텍스트가 어떻게 생성되는지 알아보겠습니다.

var g = 'this is global variable';

function fn1() {
  var o = 'this is function local variable';
}
fn1();

코드에 진입했을 때 생성된 전역 실행 컨텍스트를 나타내 보겠습니다.

'전역 컨텍스트': {
  변수객체: { 
  g: undefined, 
  fn1: <Function>
  },
  scopeChain: [GO],
  this: window,
}

1. 실행 컨텍스트가 생성된 이후 가장 먼저 스코프 체인의 생성과 초기화가 이루어집니다. 이때 스코프 체인은 전역 객체를 포함하는 리스트가 됩니다.

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

2.1. 함수 fn1 선언 처리
함수 선언은 Variable Instantiation의 단계에서 함수명 fn1Variable Object의 속성의 이름으로, 생성된 함수 객체가 값으로 설정됩니다. 여기서 중요한점은 스코프 체인에 Variable Object에 함수객체가 참조되어있기 때문에 함수 선언식 이전에도 함수 호출이 가능하게 되는 것입니다. 이것이 함수 호이 스팅이 됩니다.

2.2. 변수 g에 대한 선언 처리

1) 선언 단계(Declaration phase)
Variable Object에 변수 등록
2) 초기화 단계(Initialization phase)
Variable Object에 등록된 변수를 메모리에 할당, 이 단계에서 변수는 undefined로 초기화.
3) 할당 단계(Assignment phase)
undefined로 초기화된 변수에 실제값을 할당.
var 키워드로 선언된 변수는 선언 단계와 초기화 단계가 한 번에 이루어집니다. 따라서 스코프 체인이 참조하는 Variable Object에 변수가 등록되고 변수의 값은 undefined로 초기화되어, 변수 선언문 이전에 변수에 접근하여도, Variable Object에 변수가 존재하여 에러를 발생시키지 않는 이유입니다. 이것이 바로 변수 호이스팅입니다.

3. this 처리
변수의 선언 처리가 끝나면 다음은 this 값이 결정됩니다. this의 값이 결정되기 이전에는 전역 객체를 가리키고 있다가, 함수 호출 패턴에 의해서 this에 값이 결정됩니다.

실행 컨텍스트의 실행과정

지금부터는 코드의 실행가 실행될 때 실행컨텍스트에서 어떤 일들이 생기는지 확인해보겠습니다.

var g = 'this is global variable';

function fn1() {
  var o = 'this is function local variable';
}
fn1();

1. 변수 값의 할당
현재 실행 컨테스트의 스코프체인이 참조하고 있는 Variable Object(인덱스 0)를 시작으로 검색하여 변수명 g에 해당하는 속성이 있다면 문자열 'this is global variable'이 변수 g의 값으로 할당하게 됩니다.

2. 함수 fn1 실행 컨텍스가 생성
전역 코드에서 함수 fn1가 호출되면 새로운 함수 실행 컨텍스가 생성됩니다. 함수 fn1의 실행 컨텍스트로 컨트롤이 이동하면

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

2. Variable Instantiation 실행

3. this value 결정 이 순차적으로 일어나게 됩니다.

함수 fn1가 호출될 때의 함수 실행 컨텍스트를 아래와 같이 표현하겠습니다.

함수실행컨텍스트 = {
 ScopeChain: [AO, GO],
  변수객체: {
    arguments: null,
    o: undefined,
  },
  this: window,
}

2.1. 함수 fn1 함수실행컨텍스트의 스코프 체인의 생성과 초기화
우선 Activation Object를 스코프 체인의 첫 번째 인덱스(0)에 위치시킵니다.

2.2 Variable Instantiation 실행
스코프 체인의 생성과 초기화에서 생성된 Activation ObjectVariable Object로서 Variable Instantiation가 실행됩니다. Variable Object에 속성 o은 값은 undefined로 할당됩니니다..

그 후에는 전역컨텍스트의 scope chain에 포함된 0번째 인덱스의 객체(Global VO)가 함수 fn1 함수실행컨텍스의 scope chain에 푸시됩니다. 따라서 함수 fn1의 함수실행컨텍스트의 scope chainAOGlobal VO를 순차적으로 참조하게 됩니다.

2.4 this value 결정
현재 전역컨텍스에서 호출된 함수이므로 this의 value는 전역 객체입니다.

2.5 fn1 함수의 코드 실행
fn1 함수의 코드 실행하면서 지역변수 o에 값을 할당할 때, 현재 실행 컨텍스트의 스코프 체인이 참조하고 있는 AO를 선두로 검색하여 변수명에 해당하는 프로퍼티가 발견되면 값 'this is function local variable'를 할당합니다.

ES5.1 이후

es5.1 이후에서의 실행 컨텍스트는 이전와 다릅니다. 실행컨텍스트는 LexicalEnvironment componentVariableEnvironment component로 구성됩니다. 객체형태로 표현하면 아래와 같습니다.

실행 컨텍스트 = { 
<LexicalEnvironment component>
<VariableEnvironment component>  
}

LexicalEnvironmentVariableEnvironment는 아래 생성단계에서 무엇인지 확인해보겠습니다.

실행 컨텍스트는 어떻게 생성될까요?

실행 컨텍스트는 두 개의 단계로 이루어져 있습니다.
첫 번째는 생성 단계(Creation phase), 두번째는 실행단계(execution phase)입니다.

생성단계(Creation phase)

말 그대로 실행 컨텍스트가 생성하는 단계입니다. 생성 단계에서 어떤 것들이 생기는지에 대해 아는 것이 중요합니다.
첫 번째로 LexicalEnvironment component(어휘환경 컴포넌트)가 생성됩니다.
두 번째로 VariableEnvironment component(변수환경 컴포넌트)가 생성됩니다.

그럼 LexicalEnvironment에 대해서 알아보겠습니다.

  • 자바스크립트 엔진이 자바스크립트 코드를 실행하기 위해 자원을 모아둔 곳
  • 함수 또는 블록의 유효 범위 안에 있는 식별자와 그 결괏값이 저장되는 곳
  • 자바스크립트 엔진은 유효 범위 안에 있는 식별자와 그 식별자가 가리키는 값을 키와 값 쌍으로 바인드 한다.

이해를 돕기 위해 간단한 예제를 살펴보겠습니다.

let name = 'pandaman';
let age = 31;

function pandaman() {

}

Lexical Environment을 객체로 표현하면 아래와 같습니다.

렉시컬 환경 = {
  name: 'pandaman',
  age: 31,
  pandamn: <Function>
};

Lexical Environment는 3가지의 컴포넌트가 존재합니다

1) 환경 레코드(Environment Recode)
2) 외부 환경 참조(Rerence to the outer environment)
3) this 바인딩(this binding)

환경 레코드 (Environment Record)

Environment RecordLexical Environment에서 변수와 함수 선언이 저장되는 장소를 말합니다. ES5.1 이전의 변수 객체(variable object)와 비슷한 역할을 합니다.

외부 환경 참조(Rerence to the outer environment)

외부 환경 참조의 의미는 현재 실행 컨텍스의 외부의 Lexical Environment에 접근한다는 의미입니다. 즉 javascript 엔진은 현재 실행컨텍스의 변수를 찾을 때 Lexical Environment에 찾고자 하는 변수가 없다면 외부 환경까지 접근하여 변수를 찾을 수 있다는 말입니다.

This 바인딩 (This Binding)

this의 value가 결정됩니다. 전역 실행컨텍스의 this가 참조하는 값은 전역 객체가 되고, 함수실행컨텍스트의 경우는 this의 값은 함수의 호출에 따라 달라집니다.

지금까지 Lexical Environment에 대해 알아보았습니다. 지금부터는 Variable Environment에 대해서 알아보겠습니다.
Variable Environment는 위에서 말한 Lexical Environment 모든 요소들이 포함되어 있습니다. 그렇다면 왜 이렇게 구분을 해놓은 걸까요?
차이점이 있다면 LexicalEnvironment에는 함수 선언과 let, const 키워드의 변수가 저장되며, Variable Environment에는 var 키워드의 변수가 저장됩니다.

실행단계(execution phase)

실행 단계에서는 모든 변수에 대해 값이 할당되고 완료되고 코드가 최종적으로 실행합니다.

아래 예제를 통해서 실행단계를 설명드리겠습니다.

let name = 'pandaman';
let age = 31;
var job = 'developer';

function introduce(name) {
  var say = 'hi';
  alert(`${say}! i am ${name}`)
}

var test = introduce(name);

위의 코드가 실행되면, 자바스크립트 엔진은 전역에 있는 코드를 실행하기 위해 전역 실행컨텍스트를 생성합니다. 생성 단계에서 전역 실행 컨텍스트는 아래와 같은 객체로 표현해 보겠습니다.

전역 실행 컨텍스트 = {
 LexicalEnvironment: {
   EnvironmentRecord: {
     name: <uninitialized>, // 값이 할당되지 않음
     age: <uninitialized>, // 값이 할당되지 않음
     introduce: <func>,
   },
   OutterEnvironment: <null>, // 외부 실행 컨텍스가 존재하지 않음
   this: <Global Object>, // this는 전역객체
 },
 VariableEnvironment: {
   EnvironmentRecord: {
     job: undefined, // undefined 할당
     test: undefined, // undefined 할당
   },
   OutterEnvironment: <null>, // 외부 실행 컨텍스가 존재하지 않음
   this: <Global Object>, // this는 전역객체
 } 
}

실행단계에서는 변수의 할당이 완료되고 전역 실행컨텍스트는 아래와 같이 표현할 수 있습니다.

전역 실행 컨텍스트 = {
 LexicalEnvironment: {
   EnvironmentRecord: {
     name: 'pandamn', // 'pandamn'문자열 할당
     age: 31, // 31 할당
     introduce: <func>
   },
   OutterEnvironment: <null>, 
   this: <Global Object>
 },
 VariableEnvironment: {
   EnvironmentRecord: {
     job: 'job', // 'job' 할당
     test: undefined, // 함수가 종료되지않아 리턴되는 값이 없으므로 undefined
   },
   OutterEnvironment: <null>,
   this: <Global Object>
 }
}

함수 introduce()가 호출되면 introduce를 실행하기 위한 함수 실행컨텍스트가 생성됩니다. 생성 단계에서의 함수 실행컨텍스트는 아래와 같이 표현할 수 있습니다.

함수 실행컨텍스트 = {
 LexicalEnvironment: {
   EnvironmentRecord: {
    arguments: {0: 'pandaman', length: 1}, // arguments 객체
   },
   OutterEnvironment: <GlobalEnvironment>, // 외부 실행컨텍스트는 전역실행컨텍스트이므로 전역환경을 참조
   this: <Global Object>
 },
 VariableEnvironment: {
   EnvironmentRecord: {
     say: undefined, // undefined 할당
   },
   OutterEnvironment: <GlobalEnvironment>,
   this: <Global Object>
 }
}

생성단계 이후 실행단계에서는 함수에 있는 변수에 값이 할당이 이루어지고, 아래와 같이 나타낼 수 있습니다.

함수 실행컨텍스트 = {
 LexicalEnvironment: {
  EnvironmentRecord: {
     arguments: {0: 'pandaman', length: 1},
   },
   OutterEnvironment: <GlobalEnvironment>,
   this: <Global Object>
 },
 VariableEnvironment: {
   EnvironmentRecord: {
     say: 'hi',
   },
   OutterEnvironment: <GlobalEnvironment>,
   this: <Global Object>
 }
}

함수가 실행을 마치면, 리턴된 값이 변수 test에 할당되고, 전역 LexicalEnvironment는 갱신하게 됩니다. 전역 코드 실행이 완료되면 프로그램은 마치게 됩니다.

위에서 확인할 수 있는 중요한 포인트는 생성컨텍스트의 생성 단계에서 변수가 초기화될 때 letconstuninitialized로 값이 없지만, var의 경우에는 초기화와 동시에 undefined값이 할당됩니다. 이러한 이유 때문에 변수 호이스팅이 일어나게 됩니다.

ES5.1 이후에서의 ScopeChain

위에서 ES5.1 이전의 스코프 체인은 현재 실행 컨텍스트의 Variable Object를 시작으로 상위 실행 컨텍스트의 Variable Object를 검색하여 원하는 변수의 값을 찾습니다.
ES5.1 이후에서는 Lexical Environment의 요소인 Outter Environment에 의해서 스코프 체인이 이루어집니다.

간단한 예제를 통해 알아보겠습니다.

let v1 = 'i am global variable'
function outter() {
  let v1 = 'i am outter variable';
  (function inner() {
    console.log(v1);
  })()
}
outter();

코드가 실행되면 실행컨텍스가 생성되고 실행됩니다. 전역 실행컨텍스를 아래와 같이 표현했습니다.

GlobalExecutionContext = {
 LexicalEnvironment: {
  EnvironmentRecord: 
   v1: 'i am global variable'
   outter: <Function>
  },
  OutterEnvironment: null,
  this: <Global Object>
 },
 VariableEnvironment: {
  EnvironmentRecord: {
  },
  OutterEnvironment: null,
  this: <Global Object>
 }
}

전역 실행컨텍스트의 OutterEnvironment는 null입니다.

아래는 함수 outter가 호출될 때 생성되는 함수실행컨텍스트입니다.

FunctionExecutionContext = {
 LexicalEnvironment: {
  EnvironmentRecord: {
   v1: 'i am outter variable'
   inner: <Function>
  },
  OutterEnvironment: <GlobalLexicalEnvironment>,
  this: <Global Object>
 },
 VariableEnvironment: {
  EnvironmentRecord: {
  },
  OutterEnvironment: <GlobalLexicalEnvironment>,
  this: <Global Object>
 }
}

outter가 호출될 때 생성된 함수실행컨텍스트의 OutterEnvironment는 Global Lexical Environment를 참조하고 있습니다.

다음은 inner함수가 호출될때 함수실행컨텍스트가 실행된다면 아래와 같이 표현할 수 있겠습니다.

FunctionExecutionContext = {
 LexicalEnvironment: {
  EnvironmentRecord: {},
  OutterEnvironment: <Function Lexical Environment>,
  this: <Global Object>
 },
 VariableEnvironment: {
  EnvironmentRecord: {
  },
  OutterEnvironment: <Function Lexical Environment>,
  this: <Global Object>
 }
}

OutterEnvironment는 Function Lexical Environment를 참조하고 있습니다.
inner함수에서 name은 존재하지 않기 때문에 OutterEnvironment가 참조하고 있는 outter함수의 실행 컨텍스트의 Lexical Environment에서 name이 있는지 확인합니다. 해당 실행 컨텍스트의 Lexical Environment에 name이 존재하기 때문에 에러를 발생하지 않고 'i am outter variable'이 노출되는 것을 확인할 수 있습니다.

어렵지만 알고 나면 속이 후련해지는 실행 컨텍스트에 대해 알아보았습니다. 지금까지 베이식 자바스크립에 대해 공부해보았습니다.

읽어주셔서 감사합니다.

 

파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음

Comments