JS 시리즈 - 실행 컨텍스트

개발자 하디·2022년 2월 5일
1

JAVASCRIPT시리즈

목록 보기
4/7

INTRO

이번 JS 시리즈의 주제는 바로 실행 컨텍스트 입니다.
말그대로 자바스크립트가 어떻게 변수나 함수같은 정보를 가지고 실행이 되는지 동적 언어로서의 성격을 가장 잘 파악 할 수 있는 개념입니다. 바로 알아보도록 합시다.

1. 실행 컨텍스트

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

실행 컨텍스트는 언제 생성이 되냐면

현재 컨텍스트에 해당하는 함수가 호출되는 순간

입니다.

그래서 다시 실행 컨텍스트라는 것을 정리해보면
'함수를 실행할 때 필요한 환경정보를 담은 객체'이다.

여기서 한가지 더 알아 볼것이 있다.
바로 콜스택이라는 거다.

콜스택은 '코드 실행에 관여하는 스택'이다.
즉, 현재 어떤 함수가 동작 중인지, 다음에 어떤 함수가 호출될 예정인지 등을 제어하는 자료구조 라고 생각하면 된다.

스택이기 때문에 a -> b -> c 순서 대로 들어왔다면 c -> b -> a 순서대로 스택에서 빠져나가게 된다.

스택 구조를 잘 생각해보면 한 실행 컨텍스트가 콜 스택의 맨 위에 쌓이는 순간 현재 실행할 코드에 관여하게 되는 시점임을 알 수 있습니다.

이렇게 어떤 실행 컨텍스트가 활성화될 때 자바스크립트 엔진 해당 컨텍스트와 관련된 코드들을 실행하는 데 필요한 환경정보를 수집해서 실행 컨텍스트 객체에 저장합니다.

여기서 실행 컨텍스트에 담기는 환경정보는 3가지가 있습니다.
1. VariableEnvrionment : 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보, 선언 시점의 LexicalEnvironment의 스냅샷으로, 변경사항은 반영되지 않는다.
2. LexicalEnvrionment : 처음에는 VariableEnvironment와 같지만 변경 사항이 실시간으로 반영됨.
3. ThisBinding : this 식별자가 바라봐야 할 대상 객체

여기서 VarialbeEnvrionment와 LexicalEnvironment는 현재 변경사항을 최신화 여부의 차이 말고는 서로 똑같다고 생각하면 된다.

그래서 우리는 LexicalEnvironment에 대해서 알아보도록 하겠다.

그리고 ThisBinding같은 경우에는 다음 시리즈에서 소개할 This 편에서 다루도록 하겠다.

2. LexicalEnvironment

Lexical은 '어휘의','사전의'라는 뜻을 가지고 있다.
그래서 한번 종합해보면 어휘적환경? 사전적환경? 이러하듯이

즉, 실행 컨텍스트를 구성하는 환경 정보들을 모아서 사전처럼 구성한 객체라고 생각하면 된다.

예)
내부식별자 x : 현재 값은 10이다.
내부식별자 y : 현재 값은 null이다.
외부 정보 : a를 참조한다.

LexicalEnvrionment는 environmentRecordouterEnvironmentReference로 구성이 된다.

  • envrionmentRecord
    : 현재 컨텍스트 내부의 식별자 정보
    • 현재 문맥의 식별자 정보가 수집이 된다.
    • 실행 컨텍스트가 실행 될때 제일 먼저 하는 일이 이것이다.
  • outerEnvironmentReference
    : 외부 환경을 참조하는 정보
    • 외부의 LexicalEnvironment에 대한 참조이다.
    • 즉, 현재 문맥과 관련이 있는 외부 식별자 정보.

여기서
environmentRecord - 호이스팅
outerEnvironmentReference - 스코프 체인
이라는 개념들과 깊은 관련이 있다.

이 부분은 각 항목들을 살펴보며 알아보도록 하겠다.

3. environmentRecord

현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장

  • 현재 컨텍스트와 관련된 함수의 매개변수 식별자, 선언한 함수가 있을 경우의 함수 전체, var로 선언된 변수 식별자 등이 정보에 해당합니다.

  • 여기서 코드가 실행되기전 임에도 불구하고 자바스크립트 엔진은 이미 해당 환경에 대해서 코드의 변수명들을 모두 알고 있게 됩니다.

  • '자바스크립트 엔진은 식별자들을 최상단으로 끌어올린 다음 코드를 실행 한다.' 라고 생각해도 코드를 해석하는 데에는 문제가 없습니다.

여기서 '호이스팅'이라는 개념이 나오게 됩니다.
호이스팅은 말그대로 끌어올리다 라는 의미 이며, 가상 개념입니다.
가상의 개념이라고 하는 이유는 자바스크립트 엔진이 실제로 끌어 올리는 것은 아니지만 끌어올린 것으로 생각을 하자 인것입니다.

코드로 알아보겠습니다.

function example(a){ // 수집 대상1
    console.log(a);  // (1) 1?
    var a;          // 수집 대상2
    console.log(a); // (2) undefined?
    var a = 2;      // 수집 대상3
    console.log(a); // (3) 2 ?
}
example(1); //(4)
  • 먼저 전역컨텍스트가 활성화 되면서 함수 example이 전역컨텍스트에 담기게 됩니다.

  • (4) example 함수를 호출하면서 example 함수에 대한 컨텍스트가 콜스택에 쌓이며 example 컨텍스트가 활성화가 됩니다.

  • 위에서 말했듯이 컨텍스트가 활성화가 되면 가장 먼저 envrionmentRecord에 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다고 했습니다.

여기서 살펴보면 example 함수에 식별자 매개변수 a, var a; var a = 2;의 var a들을 수집을 합니다.

여기서 중요한 것은 envrionmentRecord는 현재 실행될 컨텍스트의 대상 코드 내에 어떤 식별자들이 있는지에만 관심이 있지, 각 식별자에 어떤 값이 할당 될것인지는 관심이 없습니다.

따라서 변수를 호이스팅할때 변수명만 끌어올리고 할당 과정은 원래 자리에 남겨두게 됩니다.

호이스팅을 마친 상태

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

    a = 1;  // 수집 대상 1의 할당 (4)
    console.log(a);          // (5)
    console.log(a);          // (6)
    a = 2;  // 수집 대상 3의 할당  // (7)    
    console.log(a);         // (8)  
}
example(1);
  • 변수 a를 선언하게 됩니다. (1)

  • 변수 a를 선언하지만 이미 선언된 a가 있으므로 무시합니다.(2), (3)

  • (4) : a에 1을 할당하게 됩니다.

  • (5), (6)에 1이 출력되게 됩니다.

  • (7) : a에 2를 다시 할당합니다.

  • (8) : 2가 출력되게 됩니다.

만약 이개념을 몰랐다면 위의 코드에서 예상 했듯이 1, undefined, 2가 출력이 될것이라고 예상하겠지만 호이스팅 개념을 안뒤 살펴보면 1, 1, 2가 출력 되는 것을 확인 할 수 있습니다.

4. outerEnvironmentReference

외부 환경을 참조하는 정보

  • 외부 환경 정보를 참조하는 outerEnvironmentReference와 스코프 체인과 관련이 있다라고 위에서 언급했습니다.

그럼 스코프 체인은 무엇인가?

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

  • 만약 식별자 정보를 찾아라 라고 한다면 그 환경에서 먼저 찾아보고 없다면 외부 환경에서 찾는 행위라고 생각하면 된다.

  • 가까운 곳부터 찾아서 가장 먼저 찾아지는 것만 접근이 가능하다.

코드로 한번 확인해 봅시다.

1  var a = 1;
2  var outer = function(){
3     var inner = function(){
4         console.log(a);
5         var a = 3;
6     };
7     inner();
8     console.log(a);
9  };
10 outer();
11 console.log(a);
  • 먼저 전역 컨텍스트가 활성이 되면서 전역 컨텍스트 envrionmentRecord에 {a, outer}가 저장됩니다.

  • 1,2 번째 줄에서 a에 1, outer에 함수를 할당합니다.

  • 10번째 줄에서 outer(); 호출되면서 outer 실행 컨텍스트가 활성화가 되며 2번째 줄로 이동하게 됩니다.

  • outer 실행컨텍스트가 활성화 되면서 envrionmentRecord에 {inner} 가 저장이 됩니다. 그리고 outerEnvironmentReference에는 outer 함수가 선언될 당시의 LexicalEnvironment가 담깁니다. outer함수는 전역컨텍스트에 선언이 되었기 때문에 전역컨텍스트의 LexcialEnvironment를 참조 복사하게 됩니다.

  • 3번째 줄에서 inner 식별자에 함수를 할당하게 됩니다.

  • 7번째 줄 : inner() 함수를 실행하면서 inner 컨텍스트가 활성화 됩니다. inner 컨텍스트가 활성화되면서 envrionmentRecord에 {a}가 저장되며 outerEnvironmentReference에는 inner함수가 outer 컨텍스트에 선언이 되었기때문에 outer컨텍스트의 LexicalEnvironment를 참조 복사하게 됩니다.

  • 4번째 줄 : a를 출력하라고 했는데 현재 inner 컨텍스트의 envrionmentRecord에 a를 검색하지만 아무 값이 검색이 되지 않았기 때문에 undefined를 출력하게 됩니다.

  • 5번째 줄 : a에 3을 할당하게 됩니다. 그리고 inner() 종료하게 되며 inner 컨텍스트는 콜스택에서 나오게 됩니다.

  • 8번째 줄: 식별자 a에 접근하고자 해서 outer envrionmentRecord에서 a를 검색합니다. 근데 현재 outer Envrionment에는 {inner} 밖에 없어 검색이 되지 않아 이때 스코프 체인을 통해 외부 환경인 전역 컨텍스트의 LexicalEnvrionment에서 찾아보게 되고 전역 컨텍스트 LexicalEnvironment에 a가 있고 저장된 값이 1이기 때문에 1을 반환하게 됩니다. 그리고 outer함수는 종료되며 outer 컨텍스트는 콜스택에서 나오게 됩니다.

  • 11번째 줄: 식별자 a에 접근하고자 합니다. 현재 활성화 상태인 전역 컨텍스트의 environmentRecord에서 a를 검색하고 1이라는 값이 할당되어 있기 때문에 1을 출력하게 되고 전역 컨텍스트가 콜스택에서 나오게 되고 종료가 됩니다.

이로서 '전역 컨텍스트 -> outer 컨텍스트 -> inner 컨텍스트' 순으로 실행컨텍스트가 생기며 스코프 체인을 타는 순서는
'inner 컨텍스트 -> outer 컨텍스트 -> 전역 컨텍스트' 순서로 검색하게 됩니다. 내부에서 외부로 접근은 할 수 있지만 외부에서 내부로는 접근을 못합니다. 그 이유는 outerEnvironmentReference는 함수가 선언될 당시의 LexcialEnvironment를 참조 복사하기 때문입니다.

마치며

자바스크립트에는 코드를 실행하는 데 필요한 환경정보를 구성하는 객체인 실행 컨텍스트가 있으며 이 실행컨텍스트는
1. variableEnvironment
2. LexicalEnvironment
3. ThisBinding
3가지로 구성이 됩니다.

여기서 1,2번 같지만 변수의 값들에 변화가 생기면 그 정보를 최신화 여부의 차이가 있습니다.

그리고 LexicalEnvironment에는
envrionmentRecord - 호이스팅,
outerEnvironmentReference - 스코프체인
이렇게 구성이 되어있습니다.

다음 시리즈에서 This를 알아보며 ThisBinding에 대해서도 알아보겠습니다.

참고 :
1. 코어자바스크립트
2. 인프런 - 코어자바스크립트

profile
기록 저장소

0개의 댓글