[JS] 실행 컨텍스트

박먼지·2022년 11월 21일
0
post-thumbnail

처음 모던 자바스크립트 책에서 실행 컨텍스트에 대해 읽었을 땐 1도 이해가 안됐는데 그 이후에 코어자바스크립트를 읽으니까 조금 이해가 됐다.

역시..반복은 중요하다..🧐

실행 컨텍스트(execution context)

실행 컨텍스트란 실행할 코드에 제공할 환경 정보들을 모아놓은 객체를 말한다.

  • hoisting(변수를 위로 끌어올림), 외부 환경 정보, this값을 설정하는 등의 동작을 수행하기 때문에 자바스크립트의 동적 언어로서의 특징이 나타난다.
    • 쉽게 말해 지멋대로 한다는 뜻...
  • 실행 컨텍스트는 stack 구조이다.

스택이란?


먼저 들어간 것이 나중에 나오고, 가장 마지막으로 들어간 것이 가장 먼저 나오는 구조
ex) 프링글스 먹을 때 맨 위에서 부터 먹는 것

  • 실행 컨텍스트를 콜 스택에 쌓아 올렸다가, 가장 위에 쌓여 있는 컨텍스트와 관련된 코드를 실행함으로써 전체 코드의 환경과 순서를 보장한다.

실행 콘텍스트와 콜 스택 예시

// -------------------- (1) 전역 컨텍스트가 콜 스택에 담김
var a = 1;
function outer() {
  function inner() {
    console.log(a);
    var a = 3;
  }        // --------- (4) inner 실행 컨텍스트 종료, outer 실행 컨텍스트 실행
  inner(); // --------- (3) inner 실행 커텍스트 생성 후 콜 스택에 담음, outer 실행 컨텍스트 일시 중지
  console.log(a); 
}             // ------ (5) outer 실행 컨텍스트 종료, 전역 컨텍스트 실행
outer();      // ------ (2) outer 실행 컨텍스트 생성 후 콜 스택에 담음, 전역 컨텍스트 일시 중지
console.log(a);
  • 그래서 환경 정보가 몬데..?

1-1.환경 정보

1-1-1. VariableEnvironment

실행 컨텍스트를 생성할 때 VariableEnvironment에 정보를 먼저 담고, 이를 그대로 복사해서 LexicalEnvironment를 만들고 그 이후에는 LexicalEnvironment를 주로 사용하게 된다. 쉽게 말해 초깃값으로 생각하면 될 듯 하다.

  • LexicalEnvironment와 마찬가지로 environmentRecord와 outerEnviromentReference로 구성되어 있다.

1-1-2. LexicalEnvironment

  • environmentRecord

    현재 컨텍스트와 관련된 코드의 식별자 정보들이 순서대로 저장된다.

    여기서 식별자는 함수에 지정된 매개변수 식별자, 선언한 함수가 있을 경우 그 함수 자체, var로 선언된 변수의 식별자 등이 해당된다.

    아까 실행 컨텍스트란 실행할 코드에 제공할 환경 정보를 모아둔 객체라고 했는데, 실행할 코드, 즉 코드가 실행하기 전에 자바스크립트는 해당 환경 정보(ex:코드의 변수명...)들을 미리 수집한 상태가 된다.

    여기서 호이스팅이라는 개념이 등장한다!

// ex) 매개 변수와 변수에 대한 호이스팅

	  1 function a (x){
      2     console.log(x); // 예상 1
      3     var x;
      4     console.log(x); // 예상 undefined
      5     var x = 2;
      6     console.log(x); // 예상 2
      7  }
	  8  a(1);
	  function a (x){
          var x; // --- a(1)을 실행하면서 수집 대상 x가 선언됨, 수집 대상 1
          var x; // --- 위의 3번째 줄, 수집 대상 2
          var x; // --- 위의 5번째 줄의 변수명 x만 끌어올림 , 수집 대상 3
        
          x = 1; // --- 수집 대상 1의 할당 부분
          console.log(x); // 결과 : 1
          console.log(x); // 결과 : 1
          x = 2; // --- 위의 5번째 줄의 변수 할당, 수집 대상 3의 할당 부분
          console.log(x); // 결과 : 2
       }
	   a(1);
// ex) 함수 선언의 호이스팅

	  1 function a (x){
      2     console.log(b);    // 예상 undefined
      3     var b = 'bbb';
      4     console.log(b);    // 예상 bbb
      5     function b () { }
      6     console.log(b);    // 예상 b 함수
      7  }
	  8  a();
	   function a (x){
           var b;             // 위의 3번째 줄 변수 선언부 끌어올림
           function b () {}   // 위의 5번째 줄 함수 선언 전체 끌어올림, b 변수명과 함수명이 같아서 b가 함수로 바뀜
           console.log(b);    // 결과 : b 함수
           b = 'bbb';         // 위의 3번째 줄 변수 할당 부분 실행
           console.log(b);    // 결과 : bbb
           console.log(b);    // 결과 : bbb
        }
	    a();
  • 함수 선언문과 함수 표현식

    • 함수 선언문은 function 정의부만 존재하고 별도의 할당 명령이 없는 것이다.
      ex) function a () {}

    • 함수 표현식은 정의한 function을 별도의 변수에 할당하는 것이다.
      ex) var b = function () {}

    앞서 실행 컨텍스트가 정보를 수집할 때 선언한 함수인 경우 함수 그 자체를 수집한다고 했다.
    함수 그 자체가 끌어올려졌는데 아래에서 같은 변수명을 사용하는 경우 함수가 다른 값으로 덮어씌워질 수 있기 때문에 버그가 생성될 수 있고 디버깅 하기도 힘들다.
    그래서 함수 표현식을 사용하는게 좋다!

  • outerEnvironmentReference
    outerEnvironmentReference를 알아보기 전에 스코프를 먼저 araboza.

    스코프란 식별자에 대한 유효 범위이다.
    스코프 체인은 스코프를 안에서 부터 바깥으로 차례로 검색해 나가는 것을 말한다.

    이를 가능하게 하는 것이 outerEnvironmentReference이다!

    outerEnvironmentReference는 현재 호출된 함수가 선언될 당시의 LexicalEnvironment를 참조한다.

    	function A(){  // A의 outerEnvironmentReference는 전역 컨텍스트의 LexicalEnvironment를 참조
         function B(){   // B의 outerEnvironmentReference는 A의 LexicalEnvironment를 참조
           function C(){  //C의 outerEnvironmentReference는 B의 LexicalEnvironment를 참조
           };
         };
       };

    각 outerEnvironmentReference는 자기가 선언된 시점의 LexicalEnvironment만 참조하고 있으므로 가장 가까운 요소부터 차례대로 접근할 수 있고, 여러 스코프에서 동일한 식별자를 선언한 경우 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근 가능하게 된다.

    //(1) 전역 컨텍스트 environmentRecord에 {a, outer} 식별자 저장
    var a = 1;         // (2) 전역 스코프 변수 a에 1 할당
    
    var outer = function () { /* (3) 전역 스코프 변수 outer에 함수 할당 
    
                                (5) outer 실행 컨텍스트 environmentRecord에 { inner } 식별자 저장, 
                                outerEnvironmentReference는 전역 컨텍스트의 LexicalEnvironment이 담김*/
    
     var inner = function () {  /* (6) outer 스코프 변수 inner에 함수 할당
     
                                   (8) inner 실행 컨텍스트의 environmentRecord에 { a } 식별자 저장, 
                                   outerEnvironmentReference는 outer 함수의 LexicalEnvironment가 담김 */
    
       console.log(a); // (9) 식별자 a를 현재 inner 컨텍스트의 environmentRecord에서 찾는데 식별자가 있지만 할당된 값이 없어서 undefined 출력.
    
       var a = 3;      // (10) inner 스코프 변수 a에 3 할당
    
     };                // (11) inner 함수 종료, inner 실행 컨텍스트가 콜 스택에서 제거되고, 바로 아래의 outer 실행 컨텍스트가 다시 활성화 됨
    
     inner();          // (7) inner 함수 호출, outer 컨텍스트 중단, inner 실행 컨텍스트 활성화
    
     console.log(a);   /* (12) 식별자 a를 outer 컨텍스트의 environmentRecord에서 찾는데 없고, 
                          outerEnvironmentReference에 있는 전역 컨텍스트 environmentRecord로 넘어가서 a를 찾는데 a가 있어서 저장된 값 1을 출력함.*/
    
    };                  // (13) outer 함수 종료, outer 실행 컨텍스트가 콜 스택에서 제거 되고, 전역 컨텍스트가 다시 활성화 됨
    
    outer();             // (4) outer 함수 호출, 전역 컨텍스트 중단, outer 실행 컨텍스트 활성화
    
    console.log(a);      // (14) 현재 활성화 상태인 전역 컨텍스트의 environmentRecord에서 a를 검색하는데 a가 있어서 저장된 값 1 출력함.

워매 복잡한 것...

즉, 전역 컨텍스트는

LexicalEnvironment
environmentRecorda, outer

outer 컨텍스트

LexicalEnvironment
environmentRecordinner
outerEnvironmentReferenceGLOBAL LexicalEnvironment

inner 컨텍스트

LexicalEnvironment
environmentRecorda
outerEnvironmentReferenceouter LexicalEnvironment

로 이루어져 있다!

  • 전역변수 : 전역 공간에서 생성한 변수
  • 지역 변수 : 함수 내부에서 선언한 변수

    코드의 안정성을 위해 전역 변수 사용을 최소화 하자!

1-1-3. ThisBinding

  • this 식별자가 바라봐야 할 대상 객체
profile
개발괴발

0개의 댓글