1. 실행 컨텍스트❓

실행 컨텍스트를 이해하기 전에 이 녀석이 도대체 무엇인지 알아보자.

1-1. 의미

코드를 실행하는데 필요한 환경을 제공하는 객체

환경 👉 코드 실행에 영향을 주는 조건이나 상태

그닥 와닿지는 않는다 😶

1-2. 구성 요소

  • 변수 객체

  • scope chain

  • this

1-3. 실행 컨텍스트 종류

1-3-1. 전역 컨텍스트

  • 코드가 실행되면 바로 생성되며, 컨텍스트 중 제일 먼저 호출 스택(call stack) 에 저장되는 컨텍스트

  • 특정 함수가 실행되지 않는 한 전역 컨텍스트에서 실행됨

1-3-2. 함수 컨텍스트

  • 함수가 호출될 때마다 생성되어 호출 스택(call stack) 에 저장되는 컨텍스트

1-3-3. eval 함수 컨텍스트

  • eval 함수는 자신만의 실행 컨텍스트를 가진다.

  • 다만, eval 함수는 사용을 자제해야 함 ( 보안에 취약 )

이번 포스팅에선 eval 함수는 제쳐두고 전역 컨텍스트함수 컨텍스트만 다룰 것이다.

실행 컨텍스트가 자꾸 호출 스택(call stack) 에 저장된다고 되어있는데 호출 스택은 뭘까❓

2. 호출 스택(call stack)

직관적으로 보면 stack 형태로 실행 컨텍스트들을 차례대로 쌓는 바구니 같다.

호출 스택의 의미

여러 함수들을 호출하는 스크립트에서 해당 위치를 추적하는 인터프리터를 위한 메커니즘

위의 그림과 같이, 자바스크립트 코드가 실행되며 생성되는 실행 컨텍스트들을 스택 형태로 저장하는 자료구조라고 볼 수 있겠다.

이제 본격적으로 실행 컨텍스트를 이해해보자.

3. 실행 컨텍스트 이해하기🙄

3-0. 예제 코드

let momstouch = true;
var mybag = false;
function myhouse() {
    let momstouch = false;

    function myroom() {
        let pet = 'cat';
        console.log(pet); // cat
        console.log(momstouch); // false
      	console.log(mybag); // false
    }

    myroom();
}

myhouse();

3-1. 호출 스택에 쌓이는 실행 컨텍스트

  • 전역 컨텍스트는 코드가 실행되면 바로 호출 스택에 추가된다.

  • 함수 컨텍스트는 함수가 호출되면 호출 스택에 추가된다.

3-0. 예제 코드를 토대로 호출 스택에 저장되는 실행 컨텍스트들을 나타내보자.

전역 컨텍스트가 제일 먼저 호출 스택에 쌓이고,
호출된 함수 순서로 함수 컨텍스트가 호출 스택에 쌓이는 것을 알 수 있다.

그렇다면 실행 컨텍스트 내부에서는 어떤 일이 일어날까❓

3-2. 생성 단계, 실행 단계

3-2-1. 생성 단계

  • 실행 컨텍스트 생성
  • 선언문만 실행해서 Environment Record에 기록 (실행 컨텍스트에 저장)

3-2-2. 실행 단계

  • 선언문 외 나머지 코드 순차적 실행
  • 실행 컨텍스트에 저장된 정보 참조 또는 업데이트

이러한 단계들을 거쳐서 실행 컨텍스트를 구성한다.

3-3. 실행 컨텍스트 구성

3-0. 예제 코드를 기반으로 적어보겠다.

3-3-1. 전역 컨텍스트 생성🔥

👉 먼저, 전역 컨텍스트가 생성되고, 생성된 컨텍스트는 생성 단계를 거친다.

생성 단계를 거치면서 let으로 선언한 변수인 momstouch는 선언만 되고 초기화는 이루어지지 않은 것을 확인할 수 있고, var로 선언된 mybag은 선언과 동시에 undefined로 초기화가 이루어진다.

myhouse( )도 함수 선언문으로 선언되었기 때문에 호이스팅이 일어나서 그대로 생성 단계에서 실행 컨텍스트에 기록된다.

👉 전역 컨텍스트가 실행 단계로 들어가며 나머지 로직을 실행한다. 이 과정에서 함수 호출, 식별자 할당 등이 이루어진다. 즉, 초기화 되지 않아 아무 값이 없던 momstouch가 true 값으로 할당 된다.

3-3-2. 함수 컨텍스트 생성 (1)🔥

전역 컨텍스트가 생성된 이후 제일 먼저 호출되는 함수는 myhouse()이다.
따라서 호출 스택에는 전역 컨텍스트 다음으로 myhouse( ) 함수 컨텍스트가 쌓일 것이다.

그리고 쌓인 함수 컨텍스트는 생성 단계와 실행 단계를 거쳐 아래와 같이 컨텍스트를 구성할 것이다.

3-3-3. 함수 컨텍스트 생성 (2)🔥

myhouse 함수 내부에서 myroom 함수를 선언하고 호출한다.
따라서 myroom 함수 컨텍스트가 호출 스택에 쌓이게 된다.

쌓인 컨텍스트는 생성 단계와 실행 단계를 거쳐 아래와 같이 호출 스택이 완성된다.

console.log( ) 또한 함수 호출이기 때문에 호출 스택에 함수 컨텍스트로 쌓여야 하지만 편의상 console.log( )의 호출 스택 push는 생략하겠다.

이렇게 해서 실행 컨텍스트가 어떻게 만들어지고, 호출 스택은 어떻게 쌓이게 되는지 이해할 수 있게 되었다.

4. environment record, outer❓

실행 컨텍스트는 생성 단계와 실행 단계를 거치면서
변수/함수의 식별자, 선언, 초기화, 할당 등을 하여 저장한다.

저장할 때, 어디에다가 저장하는 것일까❓

4-1. 특수한 내부 객체


실행 컨텍스트는 특수한 내부 객체를 가지고 있는데, 그 중에서

  1. environment record
  2. outer

위의 두 가지에 대해 기록한다.

4-1-1. environment record

실행 컨텍스트에 저장되었던 변수/함수의 정보들은 내부적으로 모두
environment record(환경 레코드) 에 저장되었다.

변수나 함수는 특수한 내부 객체인 환경 레코드의 프로퍼티이다.

따라서 변수의 값을 변경하는 것은 환경 레코드를 참조하여 프로퍼티를 변경하는 것과 같다.

그럼 outer는 뭘까❓

4-1-2. outer

outer는 실행 컨텍스트 자신의 외부에 있는 실행 컨텍스트에 접근하기 위한 사다리라고 생각하자.

👉 그림으로 표현해보면, 아래와 같이 표현할 수 있겠다.

👉 3-0의 예제 코드를 이용해서 outer의 역할을 알아보자.

let momstouch = true;
var mybag = false;
function myhouse() {
    let momstouch = false;

    function myroom() {
        let pet = 'cat';
        console.log(pet); // cat
        console.log(momstouch); // false
      	console.log(mybag); // false
    }

    myroom();
}

myhouse();

✔ myroom 함수가 실행되면서 여러 console.log( )가 실행되는데,
차례대로 살펴보면..

console.log(pet)
  1. pet의 값을 가져오기 위해 myroom 함수 컨텍스트의 환경 레코드를 참조한다.

  2. myroom 함수 컨텍스트의 환경 레코드에서 pet의 정보를 찾았기 때문에 console.log( )를 문제없이 호출한다.

console.log(momstouch)
  1. momstouch의 값을 가져오기 위해 myroom 함수 컨텍스트의 환경 레코드를 참조한다.

  2. 환경 레코드에서 momstouch를 찾지 못한다.

  3. outer를 이용해 외부 렉시컬 환경인 myhouse 함수 컨텍스트로 이동 후 환경 레코드를 탐색한다.

  4. momstouch의 값을 찾으며 console.log( )를 문제 없이 이행한다.
    ( false 값 출력)

console.log(mybag)
  1. mybag의 값을 가져오기 위해 myroom 함수 컨텍스트의 환경 레코드를 참조한다.

  2. 환경 레코드에서 mybag을 찾지 못한다.

  3. outer를 이용해 myhouse 함수 컨텍스트로 이동 후 환경 레코드를 탐색한다.

  4. 환경 레코드에서 mybag을 찾지 못해 outer를 이용하여 전역 컨텍스트로 이동한다.

  5. 환경레코드를 탐색하여 mybag을 찾고 console.log( )를 문제 없이 이행한다.
    ( false 값 출력 )

outer를 이해하는데 굉장히 도움이 많이 되었다.

5. 스코프 체인(scope chain)

식별자를 결정할 때 활용하는 스코프들의 연결리스트

자바스크립트 엔진이 해당 스코프에서 식별자를 찾는데 발견하지 못했을 때 outer를 이용해서 외부 렉시컬 환경 즉, 외부 스코프로 이동하여 식별자를 찾는다는 것을 알 수 있었다.

이렇게 식별자를 찾기 위해 외부 스코프로 이동하며 탐색하는 현상을
스코프 체인 이라고 한다.✔

6. 변수 쉐도잉

동일한 식별자로 인해 상위 스코프에서 선언된 식별자의 값이 가려지는 현상

또 한번 같은 코드로 예시를 들어보면,

let momstouch = true;
var mybag = false;
function myhouse() {
    let momstouch = false;

    function myroom() {
        let pet = 'cat';
        console.log(pet); // cat
        console.log(momstouch); // false
      	console.log(mybag); // false
    }

    myroom();
}

myhouse();

momstouch라는 변수는 전역 컨텍스트에도 존재하고 myhouse 함수 컨텍스트에도 존재한다. myroom 함수에서 console.log(momstouch)를 실행 했을 때,
false를 출력하게 되는데 이유는

myroom 함수에서 outer를 이용해 myhouse에 도착하여 환경 레코드를 참조했을 때 momstouch라는 변수의 값을 찾았다. 이미 찾았기 때문에 굳이 전역 컨텍스트로 가서 찾으려 하지 않는다는 것이다.

이러한 현상을 변수 쉐도잉 이라고 한다.

profile
유능한 프론트엔드 개발자가 되고픈 사람😀

0개의 댓글