[JavaScript] 실행 컨텍스트

YEN·2022년 7월 20일
0

JavaScript

목록 보기
6/7
post-thumbnail

1. 실행 컨텍스트란?

  • 실행 컨텍스트: 실행할 코드에 제공할 환경 정보들을 모아놓은 객체.
    -> 자바스크립트의 동적 언어로서의 성격을 가장 잘 파악할 수 있는 개념.

실행 컨텍스트를 살펴보기 위해서는 스택과 큐의 개념을 알아야 한다.

  • 스택: 출입구가 하나뿐인 깊은 우물 같은 데이터 구조.
    -> 비어있는 스택에 순서대로 데이터 a, b, c, d를 저장했다면 꺼낼 때는 반대로 d, c, b, a의 순서로 꺼낼 수밖에 없다.
    ex) 유명한 사이트인 '스택오버플로우'의 '스택'이 바로 이것이다.
    (overflow: 몇개의 데이터를 저장할 수 있는 공간에 그 이상의 데이터를 넣으려고 할 때, 스택이 넘칠 때 에러가 발생하는 상황)

  • : 양쪽 모두 열려있는 파이프.
    -> 비어있는 큐에 순서대로 데이터 a, b, c, d를 저장했다면 꺼낼 때도 같은 순서 a, b, c, d로 꺼낼 수 밖에 없다.

[ 전체 코드의 환경과 순서 ]
① '동일한 환경'에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성

② 콜 스택에 쌓아올리기

③ 가장 위에 쌓여있는 컨텍스트와 관련 있는 코드들을 실행하는 식

이때, '동일한 환경'은 하나의 실행 컨텍스트를 구성할 수 있는 방법 (전역공간, eval()함수, 함수 등)

그렇다면 콜 스택에 실행 컨텍스트가 어떤 순서로, 어떤 순서로 코드 실행에 관여할까?

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

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

① 코드 (1) 처음 자바스크립트 코드를 실행하는 순간: 전역 컨텍스트 (=일반적인 컨텍스트)가 콜 스택에 담긴다.
-> 최상단의 공간은 코드 내부에서 별도의 실행 명령이 없어도 브라우저에서 자동으로 실행하므로 자바스크립트 파일이 열리는 순간 전역 컨텍스트가 활성화된다.

② 콜 스택에는 전역 컨텍스트 외에 다른 덩어리가 없으므로 전역 컨텍스트와 관련된 코드들을 순차로 진행하다가

③ 코드 (3)에서 outer 함수를 호출: 자바스크립트 엔진은 outer에 대한 환경 정보를 수집하여 outer 실행 컨텍스트를 생성한 후 콜 스택에 담는다.

④ 콜 스택의 맨 위는 outer 함수가 되었으므로 전역 컨텍스트와 관련된 코드의 실행을 일시중단하고 대신 outer 함수 내부의 코드들을 순차적으로 진행한다.

⑤ 다시 코드 (2)에서 inner 함수의 실행 컨텍스트가 콜 스택의 가장 위에 담기면 outer 컨텍스트를 중단하고 inner 함수 내부의 코드들을 순차적으로 진행한다.

⑥ inner 함수 내부에서 변수 a에 3을 할당하고 나면 inner 함수의 실행이 종료되면서 콜 스택에서 제거된다.

⑦ 아래에 있던 outer 컨텍스트가 콜 스택의 맨 위에 존재하게 되고, 중단했던 코드 (2)의 다음 줄 부터 이어서 실행한다.

⑧ 따라서 a의 값이 출력되게 되고 outer 함수는 종료되어 콜 스택에는 전역 컨텍스트만 남게 된다.

⑨ 그런 다음 실행을 중단했던 코드 (3)의 다음 줄부터 이어서 실행하므로 a의 값을 출력하게 되면 전역 공간에는 실행할 코드가 더 이상 남아 있지 않게 된다.

⑩ 결국 전역 컨텍스트도 제거되고, 콜 스택에는 아무 것도 남지 않은 상태로 종료된다.

실행 컨텍스트가 콜 스택의 맨 위에 쌓이는 순간이 곧 현재 실행할 코드에 관여되는 시점이다.

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

[ 실행 컨텍스트 객체에 담기는 정보 ]

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

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

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


2. LexicalEnvironment

1) environmentRecord와 호이스팅

environmentRecord에는 현재 컨텍스트와 관련된 코드의 식별자가 저장된다. (매개변수 이름, 함수 선언, 변수명 등)
-> 컨텍스트 내부 전체를 처음부터 끝까지 훑어나가며 순서대로 수집한다.

but 변수 정보를 수집하는 과정을 모두 마쳤더라도 아직 실행 컨텍스트가 관여할 코드들은 실행되기 전의 상태이다.
-> 코드가 실행되기 전임에도 불구하고 자바스크립트 엔진은 이미 해당 환경에 속한 코드의 변수명을 모두 알고 있는 상태이다.

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

  • 호이스팅: '끌어올리다'라는 의미로 변수 정보를 수집하는 과정을 이해하기 쉬운 방법으로 대체한 가상의 개념.
    -> 자바스크립트 엔진이 실제로 끌어올리지는 않지만 편의상 끌어올린 것으로 간주하자는 의미이다.

ex) 매개변수와 변수에 대한 호이스팅

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

    x = 1;  //수집 대상 1의 할당 부분
    console.log(x);  //(1)
    console.log(x);  //(2)
    x = 2;  //수집 대상 3의 할당 부분
    console.log(x);  //(3)
}
a(1);

① 2번째 줄: 변수 x를 선언한다.
-> 이때 메모리에서는 저장할 공간을 미리 확보하고, 확보된 공간의 주솟값을 변수 x에 연결해둔다.

② 3번째, 4번째 줄: 다시 변수 x를 선언한다. (이미 선언된 변수 x가 있으므로 무시한다.)

③ 6번째 줄: 변수 x에 1을 할당하라고 한다.
-> 숫자 1을 별도의 메모리에 담고 x와 연결된 메모리 공간에 1을 가리키는 주솟값을 입력한다.

④ 7번째, 8번째 줄: 각 x를 출력한다.
-> (1), (2)의 결과는 모두 1이 출력된다.

⑤ 9번째 줄: x에 2를 할당하라고 한다.
-> 숫자 2를 별도의 메모리에 담고 그 주솟값을 든 채로 x와 연결된 메모리 공간으로 간다.
-> 이 공간에는 1을 가리키는 주솟값이 들어 있었는데 이것을 2의 주솟값으로 대치하게 되고 x는 2를 가리키게 된다.

⑥ 10번째 줄: x를 출력한다.
-> (3)에서는 2가 출력되고 함수 내부의 모든 코드가 실행되었기 때문에 실행 컨텍스트가 콜 스택에서 제거된다.

2) 함수 선언문과 함수 표현식

  • 함수 선언문: function 정의부만 존재하고 별도의 할당 명령이 없는 것을 의미한다.
  • 함수 표현식: 정의한 function을 별도의 변수에 할당하는 것을 의미한다.
    -> 두 가지 모두 함수를 새롭게 정의할 때 쓰이는 방식이다.

즉, 함수 선언문은 반드시 함수명이 정의돼 있어야 하고, 함수 표현식은 없어도 된다.

3) 스코프, 스코프 체인

  • 스코프: 식별자에 대한 유효범위이다.
    -> 어떤 경계 A의 외부에서 선언한 변수는 A의 외부뿐 아니라 A의 내부에서도 접근이 가능하지만, A의 내부에서 선언한 변수는 오직 A의 내부에서만 접근할 수 있다.

이러한 스코프의 개념은 대부분의 언어에 존재하지만, ES5까지의 자바스크립트는 전역공간을 제외하면 오직 함수에 의해서만 스코프가 생성된다.

  • 스코프 체인: '식별자의 유효범위'를 안에서부터 바깥으로 차례로 검색해나가는 것을 의미한다.
    -> 이를 가능하게 하는 것이 LexicalEnvironment의 두 번째 수집 자료인 outerEnvironmentReference이다.

여러 스코프에서 동일한 식별자를 선언한 경우에는 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근 가능하게 된다.

but, 스코프 체인 상에 있는 변수라고 해서 무조건 접근 가능한 것은 아니다.
-> 함수 내부에서 변수를 선언하면 전역 공간에서 선언한 동일한 이름의 변수에는 접근할 수 없다. (= 변수 은닉화)

4) 전역변수와 지역변수

  • 전역변수: 전역 공간에서 선언한 변수
  • 지역변수: 함수 내부에서 선언한 변수

0개의 댓글