실행 컨텍스트

1) 실행 컨텍스트?

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

자바스크립트는 실행 컨텍스트가 활성화되는 시점에 선언된 변수를 위로 끌어올리고(호이스팅), 외부 환경 정보를 구성, this 값을 설정하는 등의 동작을 수행합니다.

동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고 콜스택에 쌓아올립니다.
그리고 가장 위에 있는 컨텍스트와 관련된 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장하도록 한다.

💡 하나의 실행 컨텍스트를 구성하는 방법은 함수를 실행 시키는 것이다.

2) 실행 컨텍스트와 콜스택

스택 구조로 보면 결국 스택에서 맨 위에 있는 컨텍스트가 현재 실행할 코드에 관여하게 되는 시점이다.

2.1) 활성화된 컨텍스트 구조

  • VariablEnvironment
    현재 컨텍스트 내의 식별자들에 대한 정보와 외부 환경 정보, 선언 시점의 LexicalEnvironment의 스냅샷으로, 변경사항은 없음

  • LexicalEnvironment
    처음 실행 당시에는 VariableEnvironment와 같지만 변경 사항이 실시간으로 반영된다.

  • ThisBinding
    식별자가 바라봐야 할 대상 객체

3) VariableEnvironment

VariableEnvironment에 담기는 내용은 LexicalEnvironment와 같지만 스냅샷을 유지한다는 차이점이 있다.
실행 컨텍스트를 생성하게되면 VariableEnvironment에 정보를 담게 되고 그 정보를 그대로 복사해서 LexicalEnvironment 만들고 이후에는 LexicalEnvironment를 주로 활용한다.

❗️ VariableEnvironmentLexicalEnvironment 내부에는 EnvironmentRecodeouterEnvironmentReference로 구성되어 있는데 이것은 최초 초기화 과정에는 거의 완전히 동일하고 이후 코드 실행에 따라 달라지게 된다.

4) LexicalEnvironment

LexicalEnvironment는 "현재 이 컨텍스트 내부에는 이러이러한 식별자들이 있고, 그 외부 정보는 뭔가를 참조하도록 구성되어 있다" 라는 느낌으로 알고 있으면 좋은 것 같다.

4.1) environmentRecord / 호이스팅(Hoisting)

environmentRecord에는 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다.
즉 실행 컨텍스트가 활성화 되는 시점에서 내부에 있는 식별자들이 해당한다.

그리고 컨텍스트 내부 전체를 위에서부터 아래로 훑으며 순서대로 실행하게 됩니다.

에를 들어 A라는 컨텍스트에서 내부를 순서대로 훑다가 B라는 함수를 만나면 그 자리에서 중지하고 B라는 컨텍스트를 생성하게 됩니다.

여기서 한가지 호이스팅이라는 개념이 등장하는데
실행 컨텍스트가 생성될 때 environmentRecord에서는 "현재 컨텍스트의 관련된 코드의 식별자 정보들이 저장" 이 된다고 했습니다.
그렇다는 것은 코드가 실행되기 전에 이미 자바스크립트 엔진은 현재 실행된 컨텍스트에 존재하는 식별자들을 모두 알고 있게 되는 것 입니다.

  • 호이스팅
    environmentRecord는 정보 수집 과정에서 현재 실행될 컨텍스트의 대상 코드내에 어떤 식별자들이 있는지에만 관심이 있다. 그렇다는 것은 호이스팅으로 끌어올리는데 있어 실제 할당된 부분은 놔두고 변수명만 끌어올리게 된다.
// 호이스팅 전
console.log(x);
var x = 10;
// 호이스팅 후
var x;
console.log(x);
var x = 10;

4.2) 함수 선언문과 함수 표현식에서의 호이스팅

함수를 선언하는 3가지 방식

// 함수 선언문
function a () {}

// 함수 표현식 (익명 함수)
var b = function () {}

// 기명 함수 표현식
var b = function a() {}

environmentRecord에서 정보 수집 과정을 할때 호이스팅이 발생하는데 변수명만 끌어 올린다고 했습니다. 그렇다면 여기서 함수 선언문함수 표현식은 호이스팅을 할 때 어떤 차이가 발생하는지 알아보겠습니다.

console.log(sum(1,2))
console.log(divide(10,3))

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

var divide = function (a,b) {
   return a/b;
}

// 3
// Uncaught TypeError: divide is not a function

이와 같이 함수 선언문은 함수 전체가 호이스팅 되는 반면에
함수 표현식은 에러가 발생했습니다 이러한 이유는 아래와 같이 호이스팅이 되기 때문입니다.

function sum(a,b) {
   return a+b;
}
var divide;
console.log(sum(1,2))
console.log(divide(10,3))

var divide = function (a,b) {
   return a/b;
}

이와 같이 함수도 하나의 값으로 취급할 수 있습니다.

결국 함수 선언문은 함수 전체가 호이스팅이 발생하게 되고
함수 표현식은 기존과 같이 변수 선언부만 호이스팅이 일어납니다.

4.3) 스코프

스코프란 식별자에 대한 유효범위입니다. 쉽게 말하면 변수를 사용할 수 있는 영역이다.
예를 들어 A의 외부에서 선언한 변수는 A의 외부와 내부에서 접근이 가능하지만 A 내부에서 선언한 변수는 오직 A 내부에서만 접근이 가능하다.

이러한 식별자 유효범위를 안에서부터 바깥으로 나가면서 차례대로 검색해나가는 것을 스코프 체인이라고 한다. 그리고 이것을 가능하게 해주는 것이 LexicalEnvironment의 두번째 수집 자료인 outerEnvironmentReference 입니다.

4.4) 스코프체인

식별자에 대한 유효범위를 안에서부터 바깥으로 차례대로 검색하면서 나가는 것을 스코프체인이라고 했습니다.

outerEnvironmentReference는 현재 호출된 함수가 선언될 당시LexicalEnvironment를 참조한다. 이 말은 즉 A 실행 컨텍스트에서 B라는 실행 컨텍스트가 생겼을 때 A의 LexicalEnvironment를 참조한다는 말이다.

여기서 한가지 outerEnvironmentReference는 자신이 선언된 시점의 LexicalEnvironment만을 참조하고 있다. 이 말은 즉 차례대로만 접근할 수 있고 다른 순서로 접근하는 것 자체가 불가능하다는 것이다.

❗️ 위와 같은 특성에 의해 여러 스코프에서 동일한 식별자가 존재할 경우 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근이 가능하다는 것

5) ThisBinding

this는 어디서 어떻게 호출했느냐에 따라 저장되는 대상이 다른데 어떠한 대상도 지정되지 않았을 때 this는 전역 객체에 저장이 됩니다.

0개의 댓글