실행 컨텍스트

SeungMin·2022년 10월 25일
0

JAVA SCRIPT STUDY

목록 보기
4/9

📌 실행 컨텍스트란?

실행할 코드의 제공할 환경 정보들을 모아놓은 객체이다.

영어로 Excution context 이다.

기본적으로 STACK의 형태이다.

스택 형태의 특징으로는 선입후출, FILO( First In Last Out)
이라는 대표적인 특징이 있다.

스택의 구조적인 특성상 담을수 있는 한도가 있는데 이를 초과하게되면,

스택이 오버플로우되며 에러가 발생한다.



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

실행 컨텍스트가 구성되는 방법은 오직 함수가 실행될 때 이다.

// (1) 전역 컨텍스트 콜스택 추가
var a = 1;
function outer() {
  function inner() {
    console.log(a);
    var a = 3;
  }  
  // (3) inner 함수 실행, 콜스택 추가, 해당 함수 실행
  inner();
  console.log(a);
}  
// (2) outer 함수 실행, 콜스택 추가, 해당 함수 실행
outer();
console.log(a);

위의 예제와 주석문을 참고하여 흐름도를 나타내면 아래와 같다.

간단하게 설명하자면,

  1. 코드가 실행되는 순간 제일 먼저 전역 컨텍스트 객체가 콜스택에 담깁니다.
  2. 이후 함수 호출문을 만나는 순간 해당 함수를 콜스택에 추가 합니다.
  3. 추가된 함수의 내부로 이동하여 다시 코드를 순차적으로 실행 합니다.
  4. 모든 코드가 콜스택에 처리되었으면 선입후출 원리대로 콜스택에서 실행됩니다.
  5. 실행이 완료된 코드는 콜스택에서 제거됩니다.


📌 실행 컨텍스트의 세부정보

실행 컨텍스트가 콜스택에 담길 때 3가지 정보를 포함합니다.

해당 정보는 아래와 같습니다.

VariableEnvironment : 현재 컨텍스트 내의 식별자 정보, 외부 환경정보를 포함, LexicalEnvironment의 스냅샷.


LexicalEnvironment : 처음에는 VariableEnvironment 와 같지만 변경사항이 실시간으로 반영됨.


ThisBinding : this 식별자가 바라봐야 할 대상 객체

편의상 V, L 로 축약하여 서술함.

실행 컨텍스트에서 V의 역할은 L이 복사할 대상을 담당하는것.
이후로는 L을 주로 활용함.

그렇다면 처음부터 L대신 V를 사용하면 편하지 않을까?

이후 서술할 Block Scope의 경우
해당 Scope 내부의 변수 선언, 할당을 L에서 관리하고
Scope가 종료되면 원래의 L로 되돌려야 하는데
이때 V를 참조하여 되돌리게된다.



📌 호이스팅?

앞서 언급한 V, L이 가지고있는 정보가 두가지 있다.

environmentRecord : 현재 컨텍스트와 관련된 코드의 식별자 정보
outerEnvvironmentReference : 함수가 선언될 당시L 을 참조

편의상 E, O 로 축약하여 서술함.

둘 중 E가 식별자 정보를 수집하는 과정에서 발생하는 현상이 바로
Hoisting 이다.

E 에 담기는 정보는 매개변수의 이름, 함수 선언, 변수명 등이 있는데
코드 실행 이전에 해당 정보들을 미리 수집하여 알고있게 된다.

호이스팅에는 규칙이 있다.


function a (x) {
  console.log(x);
  var x;
  console.log(x);
  var x = 2;
  console.log(x);
}  
a(1);

위의 예제를 호이스팅하면 아래처럼 해석된다.


// 1. 매개변수를 변수 선언/할당과 같이 간주하여 변환.

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

--------------------------------------------

// 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 , 2

선언 대상에 함수가 있으면 결과는 조금 다르다.


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

해당 코드를 호이스팅 하면..


// 1. 호이스팅

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


----------------------

// 2. 호이스팅이 끝난 함수 선언문은 함수명 변수에 할당한 함수와 같음.


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

----------------------

출력 : function b, "bbb", "bbb"

함수와 변수가 혼용되어있는 Scope에서는 호이스팅 예측을 유의해야한다.

끔찍한 예를 들어보자.

.
.
30 | function sum (a,b){
31 |   return a + b;
32 | }
.
.
800| function sum (a,b){
801|   return (a + "+" + b);
802| }
.
.

위의 코드를 원래 사용하던 개발자 A가sum 이라는 덧셈 함수를 만들어서
아주 잘 사용하고 있었다.

이후에 B라는 개발자가 입사하여 sum 이라는 같은 이름의 함수를
덧셈의 식을 나타내는 문자열을 반환하는 함수로 선언하여 사용해버렸다.

하지만 아무 오류도 발생하지 않기때문에 어디부터 잘못된건지 찾을수가 없다.

위의 코드처럼 선언된 함수들은 전역의 최상단으로 호이스팅 되기 때문에
30번 라인에서 선언한 함수는 800번 라인에서 재할당되어
sum을 호출한 모든 라인에서 800번 라인에서 작성된 함수로 실행된다.

만약 아래와 같이 선언했다면 30번 라인 이후부터는 덧셈함수가,
800번라인 이후부터는 문자열반환 함수가 호출되었을 것이다.

.
.
30 | var sum = function (a,b){
31 |   return a + b;
32 | }
.
.
800| var sum = function (a,b){
801|   return a.toString() + "+" + b.toString();
802| }
.
.

호이스팅의 특성을 이해하고 함수 선언문함수 표현식
잘 구분하여 사용하는게 중요하다.



📌 스코프, 스코프 체인?

Scope : 식별자에 대한 유효범위
Scope Chain : 식별자의 유효범위를 안에서 바깥으로 검색해 나가는 것.

식별자의 유효범위를 검색할수 있게해주는 요소가 바로

outerEnvvironmentReference , O 이다.

앞서 언급했듯 O는 상위 스코프의 L을 참조한다.
물론 상위 스코프의 L에 있는 O 또한 더욱 상위 스코프의 L을 참조한다.

이런 구조적 특성 (Linked List) 때문에
무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근이 가능하다.


변수 은닉화?


var a = 1;
var outer = function () {
  var inner = function () {
    console.log(a);
    var a = 3;
  };  
  inner();
  console.log(a);
};  
outer();
console.log(a);

위의 코드가 실행되면
undefined, 1 , 1이 출력된다.

스코프 체인에 의하면 현재 스코프에서 선언되지 않은 변수는
상위 스코프를 참조하여 값을 찾아야하는데 undefined??

inner 함수 내부에 a를 재선언 했기 때문에,
inner 내부에서 호이스팅 됐기 때문이다.

때문에 inner 함수 내부는 아래와 같이 변한 상태로 취급된다.

var inner = () {
  var a;
  console.log(a);
  a = 3;
}  

이렇게 가장 가까운 스코프에서 변수 a를 선언했기 때문에
상위 스코프로의 검색을 중지하고 가장 먼저 찾은 a를 반환하게 된다.

전역공간에서 선언한 동일한 이름의 변수에 접근할 수 없게되는 셈이다.
이를 변수 은닉화라고 한다.

📝 정리

  • 실행 컨텍스트는 실행할 코드의 제공할 환경 정보들을 모아놓은 객체이며
    STACK 구조이다.

  • 콜스택에 추가되는 방법은 함수의 호출이다.

  • 실행 컨텍스트에는 VariableEnvironmentLexicalEnvironment가 있다.

    • VariableEnvironment 는 현재 컨텍스트 내의 식별자들에 대한 정보, 외부환경정보가 담긴다.

    • LexicalEnvironmentV를 복사한 객체이며 스코프 내부의 변수 선언, 할당 등으로 인해서 달라진다.

    • V, L은 각각 아래의 요소를 포함한다.

      • environmentRecord : 현재 컨텍스트와 관련된 코드의 식별자 정보

        • E를 구성하며 식별자 정보를 수집하는 과정에서 호이스팅 발생
      • outerEnvvironmentReference : 함수가 선언될 당시L 을 참조

  • 함수 선언문 보다는 함수 표현식이 안전하고 관라하기 쉽다.

  • 스코프 체인은 outerEnvvironmentReference를 통해서 가능하다.

  • 전역 변수를 지역 스코프에서 재선언하면 해당 변수는 은닉화 된다.

profile
공부기록

0개의 댓글