javascript - 실행 컨텍스트

김동하·2020년 9월 25일
0

javascript

목록 보기
29/58

실행 컨텍스트(execution context)는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체다. 실행 컨텍스트가 활성화되는 시점에서 선언된 변수를 위로 올리고(호이스팅) 외부 환경 정보를 구성하고 this 값을 설정하는 등 동작을 수행한다.

동일 환경에 있는 코드를 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고 이를 콜 스택에 쌓았다가 컨텍스트와 관련 있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장한다. "동일 환경"이란 하나의 실행 컨텍스트를 구성할 수 있는 방법으로 전역공간, eval(), 함수 등이 있다. 흔히 우리가 실행 컨텍스를 구성하는 방법은 함수를 실행하는 것이다!

스택(stack) 은 선입후출, 가장 처음 들어간 것이 가장 마지막에 나온다. 큐(queue)는 선입선출, 가장 처음에 들어가면 순차적으로 나온다
var a = 1;

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

코드를 실행하면 전역 컨텍스트가 콜 스택에 담긴다. 그리고 outer 함수를 호출하면서 자바스크립트 엔진은 outer에 대한 환경 정보를 수집해 outer 실행 컨텍스트를 생성하고 콜 스택에 담는다. 콜 스택의 맨 위에 outer 실행 컨텍스트가 놓인 상태가 됐으므로 전역 컨텍스트 관련 코드는 실행을 중단하고 outer 실행 컨텍스트와 관련된 코드, 즉 outer 함수 내부의 코드들을 순차로 실행한다.

inner 함수 내부에서 a 변수에 값을 할당하면 inner 함수의 실행이 종료되면서 콜 스택에서 제거된다. 그렇게 전역 컨텍스트까지 쌓인 역순으로 실행된다.

실행 컨텍스트가 활성화될 때 자바스크립트 엔전은 해당 컨텍스트에 관련된 코드들을 실행하는 데 필요한 환경 정보들을 수집해서 실행 컨텍스트에 저장한다. 그 정보들은 다음과 같다

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

LexicalEnviroment: 처음에는 VariableEnvironment와 같지만 변경 사항이 실시간 반영된다.

ThisBinding: this 식별자가 가리키는 대상

VariableEnvironment 와 LexicalEnviroment

실행 컨텍스트를 생설할 때 VariableEnvironment에 정보를 담은 다음 이를 그대로 복사해서 LexicalEnviroment를 만들고 이후에는 LexicalEnviroment을 주로 활용한다. VariableEnvironment와 LexicalEnviroment의 내부에는 enviornmentRecord와 outer-EnvironmentReference로 구성되어 있다.

LexicalEnviroment가 중요한 까닭은 변수나 함수의 값은 LexicalEnviroment이 어딘지, 즉 어디서 선언했는지에 따라 결정되기 때문이다.

호이스팅

enviornmentRecord에는 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다. 컨텍스트를 구성하는 함수에 지정된 매개변수 식별자, 선언한 함수가 있을 경우 그 함수 자체, var 변수 식별자 등이 식별자에 해당된다. 컨텍스트 내부를 처음부터 끝까지 훑는다. var 변수와 함수 선언부를 최상단으로 끌어올리는 것(실제 그러진 않음 편의상)을 호이스팅이라고 한다.

function a(x) {
    console.log(x);
    var x;
    console.log(x);
    var x = 2;
    console.log(x)
}

a(1)

호이스팅 입장에선 인자란 다른 코드보다 먼저 선언 할당이 이루어진 것이다. 다르게 말하면

function a() {
    var x = 1;
    console.log(x);
    var x;
    console.log(x);
    var x = 2;
    console.log(x)
}

a()

이 상태에서 변수 정보를 수집하는 과정, 즉 호이스팅을 처리하게 된다. 변수명과 함수 선언부를 상단에 올린다. enviornmentRecord는 현재 실행될 컨텍스트의 대상 코드 내에 어떤 식별자들이 있는지에만 관심이 있다. 각 식별자에 어떤 값이 할당되는지는 관심이 없다.

function a() {
    var x;
    var x;
    var x;
    
    x = 1;
    console.log(x); // 1
    console.log(x);  // 1
    x = 2;
    console.log(x) // 2
}

a(1)

함수가 있는 호이스팅을 살펴보자.

function a(x) {
    console.log(b)
    
    var b = "bbb"
    
    console.log(b)
    
    function b() {}
    
    console.log(b)
}
### 
a()

호이스팅을 하게 되면

function a(x) {  
    var b;
    var b = function b (){};
    
    console.log(b); // [Function: b]
    b = "bbb";
    console.log(b); // bbb
    console.log(b); // bbb
}

a()

function b가 호이스팅되면서 변수 b에 할당되었다! 변수는 선언부만 호이스팅이 되고 함수는 전체가 호이스팅 된다.

호이스팅 다른 예제

console.log('1--------'); // 1------
console.log(dongha); // undefined
console.log(say()); // hi

var dongha = 'dongha kim';
function say() {
  console.log('hi');
}

변수 dongha와 함수 say 는 선언 전 콘솔로 호출하였다. 변수는 undefined를 출력하고 함수는 정상적으로 출력된다. 이는 자바스크립트가 메모리를 할당하는 방식의 차이 때문에 발생한다.

const 선언

console.log(dongha); // ReferenceError
console.log(say()); // ReferenceError

const dongha = 'dongha kim'; 
function say() {
  console.log('hi'); 
}

변수가 메모리에 저장될 때 3단계를 거친다.

  1. 선언 단계 : 변수를 실행 컨텍스트의 변수 객체에 등록한다.
  2. 초기화 단계 : 실행 컨텍스트에 등록된 변수 객체에 대한 메모리를 할당한다. 이 단계에서 undefined로 초기화.
  3. 할당 단계 : 초기화된 값에 값을 할당한다.

var가 선언과 초기화가 동시에 진행되기 때문에 호이스팅 되면서 undefined가 되는데 let/const의 경우 선언과 초기화가 분리된다. 2단계의 경우 실제 코드가 진행되는 단계에서 진행된다 그래서 초기 값이 undefined가 아니라 ReferenceError로 출력된다.

참고 :

코어 자바스크립트
https://soldonii.tistory.com/62?category=862198
https://soldonii.tistory.com/57

profile
프론트엔드 개발

0개의 댓글