[JS 톺아보기] 실행 컨텍스트와 함께 알아본 '스코프 체인' (with. 모던 JS Deep Dive)

2024. 11. 27. 21:35·📝 공부 기록/Javascript
해당 시리즈는 [Deep Dive] 스터디의 발표 내용으로, [모던 자바스크립트 Deep Dive]의 일부를 발췌하여 작성하였습니다.

 

 

모던 자바스크립트 Deep Dive 13.3 스코프 체인 (p.195)

“스코프 체인은 물리적인 실체로 존재한다. 자바스크립트 엔진은 코드(전역 코드와 함수 코드)를 실행하기에 앞서 위 그림과 유사한 자료구조인 렉시컬 환경(Lexical Environment)를 실제로 생성한다. 변수 선언이 실행되면 변수 식별자가 이 자료구조(렉시컬 환경)에 키로 등록되고, 변수 할당이 일어나면 이 자료구조의 변수 식별자에 해당하는 값을 변경한다. 변수의 검색도 이 자료구조 상에서 이뤄진다.”

 

 

딥다이브 교재에 따르면,

자바스크립트는 렉시컬 스코프(정적 스코프)의 방식을 따르므로

함수 정의가 실행될 때 그 함수의 상위 스코프 또한 정적으로 결정된다고 한다.

이는 물리적인 실체로서 존재하고 동작한다는 것을 알 수 있다.

 

그렇다면, 여기서 알아보고 싶은 것이 있다.

 

이 물리적인 실체는 어떤 형태이며, 어떻게 상위 스코프를 결정하고 저장하는 것일까?

또한 스코프 체인은 어떤 물리적인 과정을 통해 작동하는 것일까? 이다.

 

뒤이어 한 번 더 실행컨텍스트를 다루기에 지금은 실행 컨텍스트에 대해 간단하게만 알아보며 '스코프 체인'을 중심으로 자세히 살펴보았다.

 

먼저, 실행 컨텍스트(execution context)란 무엇인가? 그리고 왜 필요한가?

자바스크립트는 런타임 실행에 앞서, 실행할 코드에 제공할 환경 정보들을 모아둔다.

이를 객체 형태로 모아두며, 이 객체를 바로 실행 컨텍스트라고 한다.

 

 

즉, 교재에도 나와 있는 물리적인 실체에 해당하는 것이 이 실행 컨텍스트이다.

 

실행 컨텍스트 안에 어떤 내용들이 있는지 보기 전에,

이 실행컨텍스트는 왜 필요한 것일까? 만약 실행 컨텍스트가 없다면 어떤 문제가 발생할 것인지와 같은 그 필요성에 대해 함께 알아보자.

 

실행 컨텍스트에는 실행할 코드에 제공할 환경 정보들을 모아둔다고 하였다.

 

만약 이런 환경 정보들을 모아두지 않는다면, 어떤 문제들이 발생할까?

1.  코드의 실행 순서와 환경을 제대로 파악하지 못한 채 런타임을 실행하면 예측 불가능한 동작이 발생할 수 있다.

2. '상위 스코프'에 대한 정보가 없어지면서 스코프 체인을 통한 식별자 검색이 불가능해지고 이는 자연스레 '클로저'와 'this 바인딩'의 올바르지 않은 작동으로 이어진다.

3. '식별자'들을 저장하고 상태 변화를 추적할 수 있는 환경이 없으므로, 동일한 코드를 여러 번 실행하였을 때의 일관된 결과를 기대하기가 어렵다.

 

즉, 실행 컨텍스트는 코드 실행에 필요한 다양한 환경 정보들을 저장함으로써

①코드 실행 순서를 보장하고, ②스코프 체인을 관리하고 ③'클로저'와 'this 바인딩'이 동작하게끔 하며  ④식별자의 상태 변화를 관리한다.

 

이 중 문제점에 해당하는 경우와 ③'클로저'와 'this 바인딩'은 다음에 더 자세히 알아보기로 하고,

이번 시간에는 ②스코프 체인을 중점으로 ①코드 실행 순서 보장과 ④식별자 상태 변화의 기능이 어떻게 동작할 수 있는지 알아보려고 한다.

 

JS는 이 실행 컨텍스트 안에 어떤 내용들을 담아놓을까?

JS가 실행 컨텍스트 안에 담아놓는 내용은 다음과 같다.

실행 컨텍스트에는 현재 컨텍스트(내부 환경)내의 식별자들에 대한 정보와 외부 환경 정보, 식별자가 바라봐야 할 대상 this 객체를 담아둔다.

 

처음에는 초깃값 공간에 내부 환경 내의 식별자 정보와 외부 환경 정보를 담아두며,

이를 그대로 복사하여 변경값 공간을 만든 후 이후에 변경이 일어날 때마다 변경값 공간에 실시간으로 반영된다.

 

처음에 사용한 초깃값 공간은 이후 변경 사항은 반영되지 않으며 최초 실행 시의 스냅샷을 유지한 채로 있다.

또한 var와 let,const의 호이스팅 차이 또한 이곳에서 일어난다. 이는 추후에 더 자세히 다뤄보겠다.

 

 

ECMAScript 2015

 

Javascript의 명세서인 ECMAScript에 따르면,

초깃값 공간은 VariableEnvironment라고 하며, 변경값 공간은 LexicalEnvironment라고 한다.

내부 환경과 외부 환경은 각각 environmentRecord, OuterEnvironmentReference라고 일컫는다.

 

즉, 실행 컨텍스트의 최종 형태는 다음과 같다.

Execution Context의 최종 형태

 

 

그렇다면 스코프 체인은 어떤 과정을 통해 동작하는가?

스코프 체인은 물리적인 환경인 실행 컨텍스트의 어떤 공간을 사용하여 어떻게 동작하는 건지 알아보려고 한다.

 

하지만 그 전에 알아야 할 것이 있다.

스코프 체인은 무엇인가?

스코프 체인은 무엇인지 알기 위해 코드를 하나 가져와보았다.

var a = 1;

var outer = function() {
	var inner = function() {
		console.log(a); // ?? ① 예측 : undefined (var 호이스팅)
		var a = 3
	}
	inner();
	console.log(a); // ?? ② 예측 : ReferenceError
}

outer();
console.log(a); // ?? ③ 예측 : 1

 

다음과 같이 외부 함수 outer가 있고, 내부 함수 inner가 있으며

각각 ①번, ②번, ③번에서 var 변수인 a를 출력한다고 하였을 때 어떤 값들이 출력될까?

 

예측을 해보자면, 먼저 가장 쉬운 ③번의 경우에는 전역 변수인 a의 값인 1이 출력될 것으로 예상된다.

①번의 경우 바로 밑 줄에서 a에 3을 할당하였지만, var 변수의 호이스팅만 일어난 채로 undefined가 출력될 것이고,

②번의 경우에는 outer 함수 내에 a가 없기때문에 ReferenceError가 나타날 것으로 예측을 해볼 수 있다.

 

하지만 실제 구현은 예측과 다르게 나타났다.

var a = 1;

var outer = function() {
	var inner = function() {
		console.log(a); // ?? ① 구현 : undefined (var 호이스팅) (에측 성공!)
		var a = 3
	}
	inner();
	console.log(a); // ?? ② 구현: 1 (예측 실패!)
}

outer();
console.log(a); // ?? ③ 구현 : 1 (예측 성공!)

 

②번의 경우에 outer 함수 내에서 a를 찾을 수 없다는 ReferenceError가 나타난 것이 아니라 전역 변수의 a를 참조한 값인 1이 출력된 것이다.

 

바로 여기서 스코프 체인이 일어난 것이다.

 

outer 함수의 스코프가 아니라 그 상위의 스코프인 전역 스코프를 참조한 예시의 코드와 같이

이처럼 상위 스코프를 따라 올라가며 참조할 변수를 찾는 것을 스코프 체인이라고 한다.

 

이제 스코프 체인이 무엇인지 알았으니, 어떻게 상위 스코프를 참조하고 변수를 검색할 수 있는 것인지

그 물리적인 실체인 실행 컨텍스트와 함께 과정을 자세히 살펴보려고 한다.

 

실행컨텍스트와 함께 스코프 체인의 과정을 살펴보자!

 

이번 스코프 체인을 설명하기 위해서 실행컨텍스트의 변경값 공간인 LexicalEnvironment (이하 L.E) 와

그 안에 있는 내부환경인 environmentRecord (이하 e),

외부환경 OuterEnvironmentReference (이하 o) 만을 사용해보려고 한다.

 

 

 

environmentRecord(E)에는 현재 컨텍스트 내의 식별자들에 대한 정보가 담긴다.

 

전역실행컨텍스트 L.E의 environmentRecord(E)에는 전역 변수 a와 함수 outer가 저장되고,

outer 함수 실행컨텍스트 L.E의 environmentRecord(E)에는 함수 inner가 저장되며

inner 함수 실행컨텍스트 L.E의 environmentRecord(E)에는 변수 a가 저장된다.

 

 

OuterEnvironmentReference(O)에는 현재 컨텍스트의 외부 컨텍스트에 대한 정보가 담긴다.

즉, 함수가 선언될 당시의 '현재 LexicalEnvironment'(외부 컨텍스트)를 담는다.

 

전역실행컨텍스트 L.E의 OuterEnvironmentReference(O)에는 상위 컨텍스트가 없기에 null이 저장되고,

outer 함수 실행컨텍스트 L.E의 OuterEnvironmentReference(O)에는 outer 함수가 선언될 당시의 L.E인 전역 컨텍스트가 저장되며

inner 함수 실행컨텍스트 L.E의 OuterEnvironmentReference(O)에는 inner 함수가 선언될 당시의 L.E인 outer 함수 컨텍스트가 저장된다.

 

스코프 체인이 과정을 그림으로 표현하면 다음과 같다.

(해당 그림은 코어 자바스크립트, 정재남, 위키북스(p.57)의 내용을 참고하였다.)

 

해당 그림에서 OuterEnvironmentReference(O)의 경우

[외부 컨텍스트의 이름, {외부 컨텍스트의 식별자 정보}] 형태로 저장하였다.

실행 컨텍스트와 스코프 체인

 

추가로, 한 줄씩 코드를 실행하는 런타임 과정과 함께 표현해보았다.

 

실행 컨텍스트의 과정이나 깊은 내용을 다루진 않았지만

스코프 체인을 이해할 수 있을 정도의 실행 컨텍스트를 다루어보았다.

 

이후, 실행컨텍스트를 보다 더 깊게 다루고 클로저와 this 바인딩의 경우도 다뤄보려고 한다.

'📝 공부 기록/Javascript' 카테고리의 다른 글
  • [JS 톺아보기] Javascript에서 module이란? 모듈 시스템의 비교까지(CommonJS, AMD, UMD, ESM) (with. 모던 JS Deep Dive)
  • [JS 톺아보기] 반복문과 재귀함수의 비교 (with. 모던 JS Deep Dive)
상심한 개발자
상심한 개발자
  • 상심한 개발자
    상심한 개발자
    상심한 개발자
  • 전체
    오늘
    어제
    • 상심한 개발자 (36)
      • 📝 공부 기록 (4)
        • Javascript (3)
        • CS (1)
        • NodeJS (0)
      • 💻 개발 기록 (1)
        • Sring, 스터디 모집 및 관리 기능 통합 서비.. (1)
      • 💾 자료구조 & 알고리즘 (26)
        • 이론 정리 (13)
        • 문제 풀이 (13)
      • 📝 후기 및 회고록 (4)
      • etc. (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    JavaScript
    삽질기록
    배열
    블로그
    array
    자료구조
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
상심한 개발자
[JS 톺아보기] 실행 컨텍스트와 함께 알아본 '스코프 체인' (with. 모던 JS Deep Dive)
상단으로

티스토리툴바