[JS] 실행 컨텍스트 (발표용)

서로·2024년 11월 4일
22

JS

목록 보기
13/15
post-thumbnail

자바스크립트 엔진

자바스크립트 엔진은 다음과 같은 두 가지 주요 구성 요소로 이루어져 있다.

  • 메모리 힙: 객체, 배열, 함수 같은 참조 타입이 메모리 힙에 할당된다.
  • 콜 스택: 자바스크립트 코드가 실행될 때 실행 컨텍스트가 쌓인다.

➊ 실행 컨텍스트

실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.

어느 시점에 콜 스택에 실행 컨텍스트가 쌓이게 될까?

다음의 예제를 살펴보자!

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

① 먼저 위의 자바스크립트 코드를 실행하는 순간 전역 컨텍스트가 생성되고 콜 스택에 담긴다.
② 그 다음에 outer 함수가 호출되는 순간 outer 실행 컨텍스트가 생성되고 콜 스택에 담긴다.
이때, 전역 컨텍스트 위에 outer 실행 컨텍스트가 쌓이는데 콜 스택이 이름 그대로 스택 구조이기 때문이다!
③ 그 다음에 inner 함수가 호출되는 순간 inner 실행 컨텍스트가 생성되고 콜 스택에 담긴다.
inner 함수 실행이 종료되며 콜 스택에서 inner 실행 컨텍스트가 제거된다.
outer 함수 실행이 종료되며 콜 스택에서 outer 실행 컨텍스트가 제거된다.
마지막 줄의 a를 콘솔에 출력하는 문까지 실행되면 더는 실행할 코드가 남지 않는다.
⑥ 이때 전역 컨텍스트가 제거되고, 콜 스택에는 아무것도 남지 않는다.

스크립트를 처음 마주할 때 전역 컨텍스트를 생성하고,
엔진이 스크립트를 쭉 읽어내려가면서 함수 호출을 발견할 때마다
함수의 실행 컨텍스트를 스택에 push 한다.

이렇게 콜 스택은 이름 그대로 스택이기 때문에 FIFO 구조를 가진다.
콜 스택에 실행 컨텍스트가 차곡차곡 쌓이면서 코드의 환경과 순서를 보장한다.

그렇다면 실행 컨텍스트에는 어떤 정보가 담길까?

VariableEnvironment, LexicalEnvironment, ThisBinding 이 담긴다.
각각에 대하여 더 세부적으로 살펴보자!

➋ LexicalEnvironment

LexicalEnvironment에는 environmentRecordouterEnvironmentReference로 구성되어 있다.

① environmentRecord

environmentRecord에는 식별자 정보가 담겨 있다.
식별자 정보는 매개변수의 이름, 함수 선언, 변수명 등이다.
위의 예시 코드에서 식별자를 찾아보자면 a, outer, inner가 될 것이다.
함수 컨텍스트가 생성될 때 함수 내의 코드 전체를 순차적으로 살펴보면서 식별자를 수집한다.
이렇게 환경 레코드에 등록된 식별자들은 코드 실행 중에 접근할 수 있게 된다.

즉, 환경 레코드는 변수와 함수의 선언을 저장하고 관리하는 역할을 한다.


여기서 호이스팅이라는 개념이 등장한다.

호이스팅이란 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징이다.

아래의 예시를 통해 호이스팅을 이해해보자.

console.log(result);
var result = 100;
console.log(result);

위의 코드는 아래의 코드와 동일하게 동작한다.

var result;

console.log(result); // undefined 출력
result = 100;
console.log(result); // 100 출력

자바스크립트에서 변수와 함수는 호이스팅 된다고 흔히 알려져 있지만
실제로는 선언문이 코드의 선두로 끌어올려져 동작하지는 않고,

식별자가 코드 실행 이전에 실행 컨텍스트의 environmentRecord에 등록된다!
이 과정에서 var 키워드로 선언된 변수는 기본값인 undefined로 초기화된다.

② outerEnvironmentReference

outerEnvironmentReference
현재 호출된 함수가 선언될 당시의 LexicalEnvironment을 참조한다.

위 문장으론 아직 outerEnvironmentReference가 무엇인지 잘 와닿지 않는다.

이를 이해하기 전에 우선 스코프의 개념에 대해 알아야 한다.

스코프란 식별자에 대한 유효 범위이다.

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

outer(); // 1 출력

console.log(a); // Uncaught ReferenceError: a is not defined

위의 예시 코드를 살펴보자!

스코프는 함수에 의해서 생성되며 outer라는 함수 내에서 선언한 a라는 변수는
오직 outer 함수 내부에서만 접근할 수 있다!

outer 함수 외부에서는 a 변수에 접근할 수 없는데
이는 식별자에 대한 유효 범위가 존재하기 때문이다!

여기서 주목해야 할 점은 다음과 같다!

inner 함수 내부에는 a라는 변수가 정의되어 있지 않으며,
outer 함수 내부에 inner 함수와 a 변수가 선언되어 있다.

그러나 inner 함수를 실행하면 a가 정상적으로 출력된다!

왜일까? 🤔

inner의 실행 컨텍스트 내부의 environmentRecord에는 변수 a가 등록되어 있지 않다.
원래대로면 a가 없기 때문에 에러가 발생하거나 undefined가 출력될 것이다.
그러나 실제로는 outer의 실행 컨텍스트로 거슬러 올라가 a를 검색하고 1을 출력한다.

이처럼 식별자의 유효 범위를 안에서부터 바깥으로 차례로 검색해나가는 것을
스코프 체인이라고 한다.

이는 inner의 실행 컨텍스트에서 outerEnvironmentReference
outerLexicalEnvironment를 참조하고 있기 때문에 가능한 일이다!

이처럼 outerEnvironmentReference는 함수가 선언될 당시의
활성화된 실행 컨텍스트의 LexicalEnvironment에 접근한다.

자신이 속한 부모 함수의 LexicalEnvironment를 참조하고 있다고 생각하면 이해가 편하다.

위의 예시 코드를 바탕으로 실행 컨텍스트를 그림으로 표현하면 다음과 같다.

➌ VariableEnvironment

LexicalEnvironment에 대한 설명을 모두 마쳤다.

다시 처음으로 돌아가서 실행 컨텍스트에는
VariableEnvironment, LexicalEnvironment, ThisBinding 이 담긴다고 했다!

VariableEnvironment에 담기는 내용은 LexicalEnvironment와 같다.

그러나 VariableEnvironment최초 실행 시의 스냅샷을 쭉 유지하며,
코드 진행에 따라 LexicalEnvironment의 내용은 달라진다는 차이점이 있다.

➍ ThisBinding

실행 컨텍스트의 ThisBinding에는 this로 지정된 객체가 저장된다.

어떤 함수를 호출할 때는,
함수로서 호출하는 경우메서드로서 호출하는 경우 2가지가 있는데
경우에 따라 this 바인딩을 다르게 한다.

var func = function (x) {
    console.log(this, x);
};
func(1);
var obj = {
    method: func
};
obj.method(2);

위의 코드에서 func이라는 동일한 함수를 그냥 함수로서 호출하는 경우
obj 객체의 프로퍼티로 등록 후 객체의 메서드로서 호출하는 경우를 확인할 수 있다.

func을 그냥 함수로서 호출하면 this는 전역 객체인 window가 된다.
그러나 프로퍼티 접근 연산자(.)로 연결하여 객체의 메서드로서 호출하면 thisobj가 된다.

실제로 자바스크립트 엔진은 점(.) 연산자의 유무로 메서드로서 호출했는지, 함수로서 호출했는지 판단한다.

🚨 어떤 함수를 함수로서 호출하면 실행 컨텍스트가 생성될 때 this 바인딩을 따로 하지 않는다.

실행 컨텍스트 활성화 당시에 this가 지정되지 않은 경우 this에는 전역 객체가 저장된다.

전역 객체에는 브라우저의 window, Node.js의 global 객체 등이 있다.

profile
읽기 쉬운 코드와 글을 작성해요 📝

5개의 댓글

comment-user-thumbnail
2024년 11월 4일

이 글을 읽고 서로선배와 결혼을 결심했습니다.
감사합니다.

2개의 답글
comment-user-thumbnail
2024년 11월 4일

서로 선배는.. 사람 맘 홀리기부터 프론트엔드 박살내기까지 못하는게 없으시군요.
오늘도 설레고 갑니다.
사랑해요.

1개의 답글