Javascript

[Javascript] 실행 컨텍스트

봄나물소녀 2021. 3. 28. 14:49

책을 읽어봐도 책장을 넘길땐 끄덕거리지만

막상 스스로에게 설명을 해보자니 도저히 정리가 안되는 느낌이 들었다.
시작 전 난 이해를 못 한거라고 분명히 지장을 찍어 둔다.

내 앞에 앉은 코딩을 막 시작한 친한 친구에게 설명하듯이 글을 써보고자 한다.

(어려운 단어는 최대한 자제할 것이다. 나도 제대로 모르니까..)

 

 

실행 컨텍스트

JS 엔진이 코드를 실행하기 위해선 무엇이 필요할까?

코드를 실행하기 위해서 JS 엔진은 그 코드의 정보들을 필요로해.

우선 식별자라는 기본 정보를 수집하고 식별자의 유효범위까지를 파악해서 순차적으로 코드를 실행하는거야. 

각각 정보들을 좀 더 자세히 봐볼까.

더보기
더보기
  1. 변수 : 전역변수, 지역변수, 매개변수, 객체의 프로퍼티
  2. 함수 : 일반적으로 함수 표현식을 말한다. e.g.) var a = function b( ){ }
  3. 스코프 : 변수의 유효범위
  4. this

 

그 다음 이런 정보들을 수집하고 나면 무엇을 할까?

 

실행하기 전에 정보들을 쭉 수집했으니 실행을 해야지. 

실행할 코드에 쓰일 정보들을 모아놓은 객체를 실행 컨텍스트라고 하는데, 이 컨텍스트는 마구잡이로 실행되는게 아니야. 마치 겨울에 눈이 아래서부터 쌓이고 쌓이고 햇살이 들면 위에 쌓인 눈부터 차례차례 녹아 없어 지는걸 상상해봐.

예를 들면 걍 자바스크팁트 파일을 연 순간에 전역 컨텍스트라는 것이 맨 처음 쌓여. 그 뒤로는 함수가 실행되는 순간에 새로운 컨텍스트들이 콜 스택에 쌓이는 식이지.

스택이 뭐냐고? 정확히는 Call stack이라고 부르는 데이터 저장 방식이야.

개봉된 택배 박스를 생각해봐. 판매처에서 박스 안에 물건을 a,b,c,d 순서로 넣었지만 우리집에 도착해서는 내가 d,c,b,a순서로 꺼내게 되잖아. 그런 데이터 구조를 Call stack이라고 불러. 이해됐어?

 

대박. 

 

    

 

 함수가 실행될 때 스택이 쌓이고 끝날 땐 스택이 빠지게 돼.

자, 그럼 스택 구조를 잘 생각해보면 한 실행 컨텍스트가 콜 스택의 맨 위에 쌓이는 순간이 곧 현재 실행할 코드라는 걸 알 수 있어.

맞아. 기존의 컨텍스트는 새로 쌓인 컨텍스트 보다 아래에 위치할 수 밖에 없기 떄문이지.

그럼 조금 더 들어가 볼까. 

실행컨텍스트는 세 가지로 구성되어 있어.

 

실행컨텍스트의 구성

 

Variable Environment 는 쉽게 말해서 처음 상태 그대로의 사진을 찰칵 찍어 놓는 스냅샷 같은거야.

그리고 Lexical Environment에 그 상태 그대로를 복사해서 본격적으로 실행 컨텍스트를 돌리기(?) 시작하지.

컨텍스트 내부의 식별자들을 쑥 훑어서 수집하고(environmentRecord)

실행에 필요한 정보를 외부 scope에서 따오게 돼(outerEnvironmentReference).

그러고 나면 하나의 스택에 빠지게 되는거지.

용어엔 집착하지말고 우선 흐름을 이해하는게 좋아.

우선 LexicalEnvonment에 관해서만 볼거야. 그 이유는 우리가 배우는 내용에 최초의 스냅샷보단 그 후에 실행되며 변경되는 것들이 중요하니까.  

LexicalEnvonment 안의 정보를 모으는 두 가지 방법을 보자.

 

environmentRecord 와 hoisting

 

현재 실행되는 컨텍스트 안에는 식별자 정보들이 저장되어 있어. 컨텍스트 내부 전체를 처음부터 쭉 훑어나가며 순서대로 정보들(변수, 함수 등)을 수집하는데 이 과정이 끝나더라도 실행은 아직 되지 않은 상태야.

 

여기서 JS에서 중요한 개념 중 하나인 호이스팅(hoisting)이 등장해!

 

친구 : hoisting이라면 '들어올리다, 끌어올리다' 할때 그 hoist구나.

 

나: 응 단어 그대로 모은 식별자들만을 최상단으로 끌어올려. 그리곤 코드를 실행하게돼. 실제로는 자바스크립트 엔진이 이렇게 작동하진 않지만.. 편의상 작동원리를 이해하기 쉽게 설명하기 위해 들여온 개념이야.

environmentRecord에서는 식별자를 모른다고 했지? 이때 hoisting이 일어나며 식별자들만을 위로 올리게돼.

 

호이스팅이 일어나지 않은 상태

function a(){
	var x =1 ; // 변수를 호이스팅할 떄 변수명만 끌어올리고 할당 과정은 원래 자리에 그대로 남겨두게 된다. 
	console.log(a);
	var x;
	console.log(a);
	var x = 'aaa';
	console.log(a);
}
a();

 

호이스팅을 마친 상태

function a(){
	var x; 					// x가 담길 데이터 그릇을 만든다.
	var x;
	var x;

	x =1 ; 					// x에 처음으로 1이 선언되었다. 
	console.log(a); // 1
	console.log(a); // 1 
	x = 'aaa';
	console.log(a); // aaa
}
a();

 

**함수 호이스팅

function a (){
	console.log(b);
	var b='bbb';
	console.log(b);
	function b(){} 
    // 호이스팅이 끝난 상태에서의 함수 선언문은 
    //함수명으로 선언한 변수에 함수를 할당한 것처럼 여길 수 있다.
	console.log(b);
}
a();

------------------------------- hoisting 후 ------------------------

	function a (){
	var b;
	var b = function b(){}

	console.log(b); // function b(){} (b함수)
	b='bbb';
	console.log(b); // bbb
	console.log(b); // bbb
}
a();

var b;
function b(){ }

}

 

outerEnvironmentReference

나 : 이 곳에선 스코프 체인을 이용해서 '식별자의 유효범위'를 안에서부터 바깥으로 차례로 검색해나가. 이를 가능하게는 것이 lexicalEnvironment의 두 번쨰 수집 자료인 outerEnvironmentRecord지. 이는 안에서는 밖이 보이지만, 밖에서는 안이 보이지 않는 코팅된 창문 이라고 비유할 수 있어. 

하위에서 상위 Lexical Environment를 참조한다는 말이야. 즉, 외부 환경에 대한 참조를 가지고 있다고 할 수 있어.

 

 

정리

 

  • 코드가 실행되면 실행 컨텍스트가 차곡차곡 쌓인다.
  • 콜스택 : 차례대로 아래에서 위로 쌓이고, 차례대로 위에서 아래로 빠지는 데이터 구조.
    **콜스택이 쌓이는 순서 
    전역컨텍스트
    -> outer함수 관련 컨텍스트 ( variable environment / lexical envirionment)

    -> inner함수 관련 컨텍스트 (위와 같음)
  • VariableEnvironment : 코드 실행 최초의 스냅샷
    LexicalEnvirionment : 식별자 정의를 위한 컨텍스트 내부의 환경 정보들을 모아놓은 것.
    *식별자 : 변수, 함수, 스코프체인, this

 

  •   environmentRecord : 식별자 수집
  •   outerEnvironmentRecord : 스코프 체인으로 식별자의 유효정보들을 수집