실행 컨텍스트

euneun·2022년 1월 31일
1

JS & TS

목록 보기
3/3

실행컨텍스트란 ?

실행할 코드에 제공할 환경정보들을 모아놓은 객체

  • 자바스크립트는 실행컨텍스트가 활성화되는 시점에 선언된 변수들을 위로 끌어올림 (호이스팅)
    === 코드가 실행되기 전임에도 불구하고, 자바스크립트 엔진은 이미 해당 환경에 속한 코드의 변수명들을 모두 알고 있게 됨
  • 그 후에 외부환경 정보를 구성함 및 this 값을 설정하는 등의 동작을 수행

실행컨텍스트를 구성하는 방법

우리가 일반적으로 구성할 수 있는 방법으로는, 함수를 실행, 호출하는것 뿐!

실행컨텍스트는 어떤 순서로 쌓이고, 어떤 순서로 코드 실행에 관여할까?

1. 자바스크립트 파일이 열리는 순간 전역 컨텍스트가 콜스택에 담겨서 활성화된다.

2. 전역 컨텍스트와 관련된 코드들을 순차적으로 실행하다가, 
함수 호출문을 만나면 해당 함수의 실행 컨텍스트가 새로 생기고 콜스택의 맨 위에 담긴다. 
따라서, 전역 컨텍스트와 관련된 코드를 중단하고 
새로 생긴 함수의 실행 컨텍스트와 관련된 코드를 실행한다.

3. 함수가 종료되면, 콜스택에서 제거하고
함수의 실행 컨텍스트가 생기면서 중단되었던 전역 공간의 코드부터 다시 실행한다.


... 반복

실행 컨텍스트에 담기는 정보

실행 컨텍스트가 콜 스택의 맨 위에 쌓이는 순간, 실행 컨텍스트가 활성화 되면서
자바스크립트 엔진은 해당 컨텍스트와 관련된 코드를 실행하는데에 필요한 환경 정보들을 수집해서 실행 컨텍스트 객체에 저장한다.

실행 컨텍스트 객체에 담기는 정보들은 다음과 같다.

  1. variable environment
    • 현재 컨텍스트내의 식별자들에 대한 정보 + 외부 환경 정보 포함
    • 선언 시점의 lexicalEnvironment의 스냅샷으로 변경사항은 반영
      되지 X
  2. lexical environment
    • 처음에는 variable environment와 동일하지만, 변경사항이 실시간으로 반영됨
  3. this binding

variable environmentlexical environment는 아래와 같이 구성됨

  • environmentRecord
    : 매개변수명, 변수의 식별자, 선언한 함수의 함수명 등을 수집
  • outerEnvironmentReference
    : 현재 호출된 함수가 선언될 당시lexicalEnvironment를 참조

Lexical environment란?

environmentRecord와 호이스팅

호이스팅?

  • 변수 정보를 수집하는 과정
  • 수집대상은 매개변수명, 변수명, 함수명

environmentRecord에 현재 컨텍스트와 관련된 코드의 식별자 정보들을 처음부터 끝까지 쭉 훑어나가며 순서대로 수집하게 되는데, 이 과정을 마치더라도 아직 코드들은 실행되기 전의 상태이다.

즉 코드가 실행되기 전임에도 불구하고, 자바스크립트 엔진은 이미 해당 환경에 속한 코드의 변수명들을 모두 알고 있게 됨

자바스크립트 엔진은, 식별자들을 최상단으로 끌어올려놓은(hoisting) 다음 실제 코드를 실행하는것처럼 보이게끔 한다!

function a(x){ // 수집대상 1 (매개변수임)
    console.log(x); 
    var x; // 수집대상 2 (변수 선언임)
    console.log(x); 
    var x=2; // 수집대상 3 (변수 선언임)
    console.log(x);
}
a(1);

을 실행하면 1,undefined,2가 차례로 찍힐 것 같지만

실제로는 호이스팅에 의해서, 아래 코드와 같이 동작한다.
(엔진이 실제로 이렇게 변환과정을 거치지는 않지만, 이해를 편하게 하기 위함임)

function a(){
  var x; // 수집대상 1의 변수 선언부분
  var x; // 수집대상 2의 변수 선언부분
  var x; // 수집대상 3의 변수 선언부분
  
  x=1; // 수집대상 1의 할당 부분
  console.log(x);
  console.log(x);
  x=2; // 수집대상 3의 할당 부분
  console.log(x);
   
}
a(1);

변수명만 끌어올리고 할당과정은 원래 자리에 그대로 남겨둠.

1,1,2가 찍힌다.

함수 선언을 추가한 예제

function a(){ 
    console.log(b);
  	var b='bbb';
  	console.log(b);
  	function b() {};
  	console.log(b);
}
a();

호이스팅을 알지 못했다면, undefined, bbb, 함수b 가 차례로 출력될거라고 생각했겠지만

위의 코드가 호이스팅되고나면, 아래 코드처럼 동작할 것이라는것을 알 수 있다.

function a(){ 
  	var b; // 변수는 선언부만 끌어올림
  	function b() {}; // 함수 선언은 전체를 끌어올림
    
  	console.log(b);
  	b='bbb'; // 할당부는 원래 자리에 남겨둠.
  	console.log(b);
  	console.log(b);
}
a();

따라서 b함수,bbb,bbb순서로 로그가 찍히게된다.

함수 선언문 vs 함수 표현식

함수 선언문

  • 반드시 함수명이 정의되어있어야 한다.
  • function 정의부만 존재하고 별도의 할당 명령이 없다.

함수 표현식

  • 함수명이 정의되지 않아도 된다.
    - 기명 함수 표현식 | 익명 함수 표현식
  • 정의한 function을 별도의 변수에 할당해야한다.

✅ 둘의 가장 중요한 차이
함수 표현식은 변수의 선언부와 할당부가 분리되어, 변수 선언부만 호이스팅 된다는것..!!
그래서 함수 선언문은 선언 전에 호출해도 함수 전체가 호이스팅되어 문제 없이 실행되지만,
함수 표현식은 선언전에 호출하면 "x is not a function" 이라는 오류가 발생하게됨!!

그래서 상대적으로 함수 표현식이 함수 선언문보다 더 안전하다..!
(발생해야할 오류를 잘 나타내주기때문)

스코프

스코프란?

식별자에 대한 유효범위

1 var a =1;
2 var outer=function(){
3    var inner=function(){
4        console.log('inner a',a);
5        var a=3;
6    };
7    inner();
8    console.log('outer a ',a);
9 };
10 outer();
11 console.log('global a',a);

위 코드의 실행결과는 어떻게 될까?

(1). 전역 컨텍스트의 environmentRecord{a,outer} 식별자를 저장
전역 컨텍스트는 선언 시점이 없으므로, 전역 컨텍스트의 outerEnvironmentReference에는 아무것도 담기지 않음

전역 컨텍스트

  • environmentRecord : {a, outer}
  • outerEnvironmentReference : X

(1)~(2). 전역 스코프에 있는 변수 a에 1을, outer에 함수를 할당.

(10). outer함수 호출. outer 실행컨텍스트가 활성화되어 2번째 줄로 이동.

(2). outer 실행 컨텍스트의 environmentRecord{inner} 식별자를 저장
outerEnvironmentReference에는 outer함수가 선언될 당시 === 전역 컨텍스트의 LexicalEnvironment를 참조복사한다.

outer 실행 컨텍스트

  • environmentRecord : {inner}
  • outerEnvironmentReference : [Global, {a,outer}]

(3). 변수 inner에 inner함수를 할당한다.

(7). inner함수가 호출되어 inner 실행 컨텍스트가 활성화되고, outer 컨텍스트는 중단되고 3번째줄로 이동된다.

(3). inner 실행 컨텍스트의 environmentRecord에 {a} 식별자를 저장.
outerEnvironmentReference에는 inner 함수가 선언될 당시 === outer함수 내부에서 선언되었으므로 outer 함수의 lexicalEnvironment를 참조복사한다.

inner 실행 컨텍스트

  • environmentRecord : {a}
  • outerEnvironmentReference : [outer, {inner}]

(4). 현재 활성화 상태인 inner 컨텍스트의 environmentRecord에 a를 검색하면, 아직 할당된 값이 없으므로 undefined를 출력한다.

(5). inner 스코프에 있는 변수 a에 3을 할당한다.

(6). inner 함수가 종료되고, 실행 컨텍스트가 콜 스택에서 제거되어 outer 실행 컨텍스트가 다시 활성화되면서 앞서 중단되었던 7번째줄의 다음으로 이동한다.

(8). 현재 활성화 상태인 outer 컨텍스트의 environmentRecord에 a가 존재하지 않으므로, outerEnvironmentReference에 있는 environmentRecord인(전역 lexicalEnvironment) a에 저장된 값인 1을 반환한다.

(9). outer 함수가 종료되고, 실행 컨텍스트가 콜 스택에서 제거되어 전역 컨텍스트가 다시 활성화되면서, 앞서 중단했던 10번째줄의 다음으로 이동한다.

(11). 현재 활성화 상태인 전역 컨텍스트의 environmentRecord에서 바로 a를 찾을 수 있으므로 1을 출력한다. 모든 코드의 실행이 완료되고, 전역 컨텍스트가 콜 스택에서 제거되고 종료한다.


출처

profile
제대로 짚고 넘어가자!🧐

0개의 댓글