[JS] 실행 컨텍스트를 통한 호이스팅과 스코프의 이해

DongDong·2022년 11월 23일
0
post-thumbnail

🔎 실행 컨텍스트(Execution Context)란?

코드를 실행하는 데 필요한 정보와 그 범위를 추상화한 객체라고 할 수 있으며
실행 컨텍스트는 scope, hoisting, this, function, closure 등의
동작원리를 담고 있는 자바스크립트의 핵심 원리입니다.

따라서 실행 컨텍스트를 이해한다면 스코프, 호이스팅, 클로저 등 자바스크립트의 주요한 동작을 이해할 수 있습니다.

이번 포스팅에서는 실행컨텍스트로 호이스팅과 스코프를 이해해보려 합니다 :)

본 포스팅은 ES5 이후의 실행 컨텍스트에 대하여 다룹니다.


실행 컨텍스트는 폴더처럼 계층형으로 이루어져있습니다. ( 스택 구조 )
그리고 자바스크립트 엔진은 스택의 최상단 실행 컨텍스트를 바라보며 동작하게 됩니다.

실행 컨텍스트가 만들어지는 경우는 총 4가지가 있는데, 아래와 같습니다.

1) 전역 코드 : 전역 영역에 존재하는 코드
- 기본적으로 자바스크립트 코드가 시작할 때 가지고 시작한다.
- 실행환경이 글로벌 실행 컨텍스트이다.
- 무조건 한 개만 존재할 수 있다. ( JS는 싱글 스레드이기 때문에 )

2) Eval 코드 : eval 함수로 실행되는 코드
- 많이 사용하지 않는 함수이며 특수한 경우가 아닌 이상 피해야하는 기능이므로 생략

3) 함수 코드 : 함수 내에 존재하는 코드
- 함수적인 실행 문맥이다. ( 실행 컨텍스트는 기본적으로 함수단위로 생성된다.)
- 함수가 호출이 되고 실행될 때 그 함수에 대해서 실행 컨텍스트이다.
- 함수 내부에서 this, 지역변수 , 등을 포함한 것이 함수 실행 컨텍스트이다.

4) 블록문 (ES6 이후부터)

이렇게 생성되어진 실행 컨텍스트는 실행 스택(Execution Stack)에 쌓이게 됩니다.

실행 스택(Execution Stack)이란?
LIFO의 자료구조로 코드나 함수가 실행되면서 생성되는 실행컨텍스트들이 저장되는 구조적 집합체를 의미한다.

Javascript 엔진이 script 태그를 만나면,

1) 실행 가능한 코드로 제어권(control)이 이동하며 논리적 스택 구조를 가지는
빈 실행 컨텍스트 스택이 생성되고

2) 제어권이 전역 코드로 이동하면 전역 실행 컨텍스트가 생성되며
비어있는 컨텍스트 스택에 push하게 됩니다.

3) 그리고 전역환경 혹은 특정 환경에서 함수를 호출하면 제어권이 함수 코드로 이동하면서 호출된 함수의 함수 컨텍스트가 생성 및 실행 스택에 쌓입니다.

즉, 최하단에서는 Root(전역 실행 컨텍스트)가 있고 그 위로 하나씩 쌓이게 됩니다.

4) Javascript 엔진은 실행 스택 에서 가장 상단에 있는 실행 컨텍스트부터 순차적으로 실행하며 각 실행 컨텍스트 가 종료되고 나면 실행 스택 에서 제거(pop) 되고, LIFO의 순서에 맞춰서 제어권이 이동하게 됩니다.

처음 전역 실행 컨텍스트가 생성된 이후로 3번과 4번을 반복하게 됩니다.


위 4가지 방법으로 만들어지는 실행 컨텍스트는 내부적으로 다음과 같은 형태로 이루어져있습니다.

[실행컨텍스트]
- Variable Environment
- Lexical Environment
- thisBinding

이렇게 3가지로 이루어져 있으며 3가지 모두 실행 컨텍스트를 참조하는 관계이며
이 중 가장 중요한 Lexical Environment만 해당 포스팅에서 다루려합니다.


📌 Lexical Environment

Lexical Environment는 또 내부적으로 다음과 같은 형태로 이루어져있습니다.

  • Environment Record
  • Outer Environment Reference

먼저 Environment Record는
컨텍스트 전체를 훑어나가며 식별자들을 수집하며 호이스팅과 직접적인 관련이 있습니다.

var a = 0; // 여기서 a가 식별자입니다.
function b(){} // 여기서 b가 식별자입니다.

자바스크립트 엔진은 코드를 실행할 때 생성단계와 실행단계로 나뉘어져 있으며 생성단계는 실행단계보다 먼저 실행되어 선언문만 먼저 환경 레코드에 기록하게 됩니다.
따라서 현재 실행될 컨텍스트의 해당하는 코드 내에서 식별자 정보만 먼저 수집하기 때문에
변수를 인식할 때 식별자만 끌어올리고 할당과정은 진행하지 않습니다.
우리는 이것을 호이스팅(Hoisting) 이라고 부릅니다.

즉 Environment Record는 현재 컨텍스트와 연관되어있는 코드들의 식별자 정보들을 저장하는 공간입니다.


📌 Outer Environment Reference

이어서 Outer Environment Reference는
자기 자신의 상위 스코프를 참조하는 공간으로서 스코프와 직접적인 관련이 있습니다.
전역 실행 컨텍스트에서는 NULL이며
현재 호출되는 함수가 선언되는 시점에서의 Lexical Environment를 참조하는 포인터입니다.
따라서 현재 함수가 속해있는 상위 스코프의 범위를 참조하고 있는 것이며
우리가 지역 스코프를 벗어난 변수를 가져오고 싶을 때 Outer Environment Reference를 사용하게 되는 것입니다.

우리가 변수를 가져오는 과정은 아래와 같습니다.

변수를 가져올 때 가장 최상위의 실행 컨텍스트를 먼저 살펴보고
최상위의 실행컨텍스트 내부의 가져오고자하는 변수가 없으면 그 바로 아래의 실행컨텍스트를 살펴보게 됩니다.
만약 여기서 가져오고자하는 변수가 있다면 더이상 그 아래로 진행하지 않고 끝내게 되지만가져오고자하는 변수가 지금 최상위의 실행컨텍스트 그 바로 아래에 있는 실행컨텍스트에도 없다면 그 아래, 또 없다면 그 아래 ,
이렇게 계층적으로 탐색해나가며 Root(전역 실행 컨텍스트)까지 찾게된다.
Root까지 탐색해나갔음에도 가져오고자하는 변수가 없다면 참조 에러가 발생하게 됩니다.

이 과정을 우리는 스코프 체이닝이라고 부릅니다.


참고한 글 :
1) https://poiemaweb.com/js-execution-context
2) http://dmitrysoshnikov.com/ecmascript/es5-chapter-3-2-lexical-environments-ecmascript-implementation/#structure-of-execution-context
3) https://joontae-kim.github.io/2020/10/12/excution-context/
4) http://dmitrysoshnikov.com/ecmascript/es5-chapter-3-2-lexical-environments-ecmascript-implementation/#structure-of-execution-context

profile
중요한건 꺾이지 않는 마음

0개의 댓글