딥다이브 스터디 23(실행컨텍스트)

김영현·2023년 10월 19일
0

컨텍스트

현재 어떤 상태에서 수행되고 있는지 정확히 파악하고자 함.

컨텍스트 === 문맥.
OS에서 프로세스도 컨텍스트를 가지듯, JS도 자신만의 컨텍스트를 가진다.
이를 실행 컨텍스트라 함.


실행컨텍스트

js의 동작원리를 담고있는 핵심 개념
이를 알면

  • 호이스팅
  • 태스크 큐와 함께하는 이벤트 핸들러 + 비동기 처리 동작방식
  • 스코프기반 식별자바인딩 값 관리하는 방식
  • 클로저

위와같은 JS의 본질적인 기능들을 보다 잘 이해하고 알수 있다.

이 파트에서 사용하는 용어중 생소한 용어들을 먼저 소개하고 넘어가겠다.

  1. 전역 코드: 전역에 존재하는 코드. 함수, 클래스 등의 내부 코드는 포함되지 않음
  2. 함수 코드: 함수 내부에 존재하는 소스코드(중첩 제외)
  3. eval 코드: eval에 인수로 전달되어 실행되는 코드
  4. 모듈 코드: 모듈 내부에 존재하는 코드. 함수 클래스의 내부코드 역시 포함x

JS는 소스코드를 이렇게 4종류로 나누었다
나누는 기준은 실행 컨텍스트를 생성하는 과정과 관리가 다르기 때문

실행컨텍스트를 생성하는 과정(개요)

전역코드
=> 전역스코프 생성해야함. var로 선언된 전역변수와 함수선언문 전역객체에 할당작업.
이를 위해 전역코드가 평가될 때 전역 실행 컨텍스트가 생성된다.

함수 코드
=> 지역스코프 생성. 매개변수,arguments객체 등 관리. 지역스코프를 스코프체인에 할당 등.
이를 위해 함수코드가 평가되면 함수 실행 컨텍스트 생성

eval 코드
=> strcit mode에서 독자적 스코프 생성. 이를 위해 eval 실행 컨텍스트 생성

모듈 코드
=> 모듈은 독립적인 스코프를 가짐. 따라서 모듈 실행 컨텍스트생성.

4가지의 방식으로 미루어보아, 스코프가 생성되면 실행 컨텍스트도 실행되는 것 같다.

소스코드의 평가와 실행

앞서, 평가되면 실행 컨텍스트가 생성된다 하였다.
여기서 평가는 코드를 실행하기위한 준비다.
=> JS소스 평가 - 소스 실행2가지 단계를 거친다!

  • 소스코드 평가: 실행 컨텍스트 생성. 변수, 함수등 선언문만 먼저 실행(식별자 등록때문)하여 실행 컨텍스트가 관리하는 스코프(Lexical Environment Record 이하 L.E.R)에 할당.
    전에 var, const, let설명하는 과정에서 const, letL.E.R에 할당된다고 들었는데, 여기구나!

  • 소스코드 실행: 소스코가 순차적으로 실행. 이때 필요한 정보들(변수, 함수의 참조)등을 실행 컨텍스트가 관리하는 스코프에서 검색하여 취득한다! 결과는 다시 실행 컨텍스트가 관리하는 스코프에 등록된다.

=> 아하, 결국 실행컨텍스트스코프는 밀접한 관련이 있구나

예제로 한번 알아보자.

var x;
x = 1;

소스 평가과정에서 var x;를 선언하고 실행컨텍스트가 관리하는 스코프에 저장(undefined로 초기화)
평가과정이 끝나면, 실행이 시작된다!
But!!!, var x는 이미 평가과정에서 끝났다. 따라서 실행과정은 x = 1;만 실행한다!

실행컨텍스트의 역할

내가 보기엔 저장소, OS 프로세스의 컨텍스트와 굉장히 유사한 개념이다.
어떤 상황인지 알아야 판단하고, 어떤 변수가 있는지 참조하는 등.
일단 예제로 알아보자

//전역변수 선언
const x = 1;
const y = 2;

//함수 정의
function foo(a){
	const x = 10;
  	const y = 20;
  
  	console.log(a + x + y) //130
}

foo(100);

console.log(x + y) //3
  1. 전역코드 평가 : 소스 평가 과정에서 본 것과 마찬가지로 전역 변수 선언, 함수선언문먼저 실행, 전역 스코프에 등록.
  2. 전역코드 실행 : 전역 변수값 할당. 함수가 호출되면 코드실행 일시중단 후함수내부진입
  3. 함수 코드 평가 : 매개변수, 지역변수지역 스코프 등록. arguments객체 생성 및 this바인딩
  4. 함수 코드 실행 : 매개변수, 지역변수값 할당. console.log메소드 호출하기위해 console스코프 체인 통하여 검색. But, 스코프체인에 없음(전역객체 프로퍼티는 그냥 쓸수있다).
    그다음 console객체의 프로토타입 체인을 타며 log메소드 검색.
    이후 전달된 표현식a + x + y평가.
    또 스코프체인타고....함수호출끝. 전역코드 실행 진행!

이처럼 코드가 잘 진행되려면, 스코프, 식별자, 코드 실행 순서 등의 관리가 필수임
=> 총망라하는게 실행 컨텍스트구나!

식별자, 스코프는 실행컨텍스트의 Lexical Enviroment로 관리하고, 코드실행은 실행컨텍스트 stack으로 관리한다.
=> 마치 함수가 쌓이는 call stack과 유사하군! 함수, 실행컨텍스트라는 쌓이는 본체가 다르지만.

실행컨텍스트 스택

코드 실행실행 컨텍스트 스택으로 관리한다 하였다.
다시말해, 어떠한 함수로 진입할시 컨텍스트스택처럼 쌓인다

const x = 1;

function foo() {
	const y = 2;
  
  	function bar() {
    	const z = 3;
      	console.log(x + y + z);
    }
  	bar();
}

foo(); //6

전역 실행컨텍스트 => foo가 실행되어 foo실행 컨텍스트 => 내부에서 bar가 실행되어 bar실행컨텍스트 => 실행되고 빠져나감. => foo가 실행되고 빠져나감 => 전역실행컨텍스트 => 종료

콜 스택을 찾아보니 실행컨텍스트를 담는 자료구조였다!

렉시컬 환경(Lexical Environment)

어휘적 환경. 참조에 대한 변수들을 저장한 곳이라고 보면 된다.
식별자, 식별자에 바인딩된 값, 상위스코프에 대한 참조가 기록되어있다.

위의 예제에서 foo()상위스코프(전역)에 대한 참조를 갖고있어 x값을 알 수 있다.

렉시컬 환경은 두개로 또 나뉜다..!

  • Environment Record : 위에 설명한 식별자등록과 값을 관리함
  • Outer Lexical Environment Reference : 상위 스코프를 가리킨다!(상위코드의 렉시컬 환경)
    당연하게도 단방향 링크드 리스트로 구현되어있다.

실행 컨텍스트의 생성과 식별자 검색 과정

예제를 또..보고싶은데, 너무 길다.
과정만 짚고 넘어가보자.

  1. 전역객체 생성: 전역 코드가 평가되기 이전 생성(전에 설명했던 프로퍼티들 포함).
  2. 전역코드 평가
    E.C(Excution Context), L.E(Lexical Environment), E.R(Environment Record).
  • 전역 E.C 생성
  • 전역 L.E 생성
  • 전역 E.R 생성
  • 객체 E.R 생성
  • 선언적 E.R 생성
  • this 바인딩
  • 외부 L.E에 대한 참조 결정.
    이러한 순서로 전역코드평가가 진행된다.

여기서 전역 E.R생성부터 파헤쳐보자

  1. 전역 E.R
    본래 전역객체였음. 하지만 const, let으로 선언한 변수는 전역객체에 들어가지 않음
    따라서 전역E.R내부에 Object Environment Recod, Declarative Environment Record로 나뉨.
    각각 기존 전역객체가 관리하던 것과, let,const관리 용도다.

  2. 객체E.R
    1번에서 설명한 전역객체다. window없이 프로퍼티를 기술할수 있는게 이친구 덕분이다.
    호이스팅은 여기서 일어난다.

  3. 선언적 E.R
    1번에서 설명한 let,const를 위한 환경이다.
    선언,초기화단계가 분리되어 진행됨. TDZ에 빠진다.

  4. this 바인딩
    전역 E.R 내부슬롯 [[GlobalThisValue]]this가 바인딩 된다.
    일반적으로 전역코드에서 this는 전역 객체.
    참고로 2,3번에는 this바인딩이 없다.

  5. 외부 L.E에 대한 참조 결정
    상위 스코프를 가리킴. 전역에서 외부 L.E는 null이다.

  6. 전역 코드 실행
    스코프체인은 사실외부L.E를 뒤적이는 작업.

함수 평가-실행과정

함수는 호출 위치가 아닌 정의된 위치의 상위 L.E를 참조한다.
=> 클로저를 이해할 수 있는 중요한 단서다! 참조하는동안 G.C가 지우지 않기에 외부함수의 변수를 사용한다.

하지만 this호출한 방식에 따라 결정된다! 이를 헷갈리지 말자.

블록레벨 스코프

블록레벨 스코프는 결국 블록 내부의 L.E를 새롭게 생성하여, 기존의 전역 L.E를 교체한다.
이때 블록 내부의 외부L.E참조는 당연히 실행 직전 환경이다.

느낀점

내부적인 요소를 확실히 알아간다.
특히 호이스팅클로저 스코프 체인등의 깊은 내용의 동작원리를 완벽히 이해하게 됐다.
정말 너무 지엽적이고 쓸모없어 보였는데, 막상 공부해보니 Js를 더 잘 알게되었다.
이제 thisnode조작등만 더 공부하면 이론은 완벽하겠군!
이젠 실전을 많이해보자!

profile
모르는 것을 모른다고 하기

0개의 댓글