JavaScript - 실행 컨텍스트

JavaScript - 실행 컨텍스트

“실행할 코드에 제공할 환경 정보들을 모아놓은 객체로, 자바스크립트의 동적 언어로서의 성격을 가장 잘 파악할 수 있는 개념”

스택(Stack)

  • 출입구가 하나뿐인 깊은 우물 같은 데이터 구조
  • 비어있는 순서대로 데이터 1,2,3를 저장(Push)했다면, 꺼낼때는 반대로 3,2,1의 순서로 꺼내는 것(Pop).

큐(Queue)

  • 양쪽이 모두 열려있는 파이프 구조
  • 종류에 따라 양쪽 모두 입력과 출력이 가능한 큐도 있으나, 보통은 한쪽은 입력만, 다른 한쪽은 출력만을 담당하는 구조

실행 컨텍스트의 동작

  • 동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트에 구성
  • 컨텍스트들을 순서대로 콜 스택(Call Stack) 에 쌓아올림
  • 가장 위에 쌓여있는 컨텍스트와 관련 있는 코드들을 실행
  • 전체 코드의 환경과 순서를 보장 👉🏻 콜 스택(Call Stack) : 자바스크립트 엔진이 구동되면서 실행 중인 코드를 추적하는 공간
  • 함수(Function)의 호출(Call)을 기록하는 스택(Stack)자료구조

실행 컨텍스트의 구성방법

  • 전역공간(자동으로 생성)
  • eval()함수(악마로 취급)
  • ✨ 함수 (가장 흔한 실행 컨텍스트 구성방법)
  • ES6에서는 블록{}에 의해서도 새로운 실행 컨텍스트 생성

실행 컨텍스트와 콜스택

// ---------------(1)
var a = 1;
function outer() {
  function inner() {
    console.log(a); //undefined
    var a = 3;
  }
  inner(); // ---------------(2) inner 함수 컨텍스트
  console.log(a); // 1
}
outer(); // ---------------(3) outer 함수 컨텍스트
console.log(a); // 1

자바스크립트 코드를 실행하는 순간

  • 1️⃣ (1) 전역 컨텍스트가 콜 스택에 담김
  • 2️⃣ 콜스택에는 전역 컨텍스트 외에 다른 것이 없으므로 전역 컨텍스트와 관련된 코드들을 순차로 진행
  • 3️⃣ (3) outer 함수를 호출하면 자바스크립트 엔진은 outer에 대한 환경정보 수집 -> outer 실행 컨텍스트를 생성한 후 콜스택에 담음
  • 4️⃣ 콜스택의 맨 위에 outer 실행 컨텍스트가 놓은 상태로, 전역 컨텍스트와 관련된 코드의 실행을 일시중단
  • 5️⃣ outer 함수 내부의 코드들을 순차로 실행
  • 6️⃣ (2) inner 함수의 실행 컨텍스트가 콜 스택의 가장 위에 담기면 outer 컨텍스트와 관련된 코드의 실행 중단
  • 7️⃣ inner 함수 내부의 코드를 순서대로 진행

    👉🏻 자바스크립트 엔진은 해당 컨텍스트에 관련된 코드들을 실행하는 데 피룡한 환경정보들을 수집해서 실행 컨텍스트 객체에 저장

실행 컨텍스트의 수집 정보

  • VariableEnvironment : 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부환경 정보(선언 시점의 LexicalEnvironment의 스냅샷으로, 변경사항 반영 X)
  • LexicalEnvironment : 처음에는 VariableEnvironment와 같지만 변경 사항이 실시간으로 반영
  • ThisBinding : this 식별자가 바라봐야 할 대상 객체

VariableEnvironment

  • LexicalEnvironment와 같지만 최초 실행 시의 스냅샷 유지
  • 실행 컨텍스트 생성 시, VariableEnvironment에 정보를 먼저 담은 다음, 이를 그대로 복사해 LexicalEnvironment 생성, 이후 LexicalEnvironment 주로 활용
  • environmentRecord와 outer-EnvironmentReference로 구성

LexicalEnvironment

  • ‘현재 컨텍스트의 내부에는 a,b,c와 같은 식별자들이 있고, 그 외부 정보는 D를 참조하도록 구성돼있다’라는, 컨텍스트를 구성하는 환경 저보들을 사전에서 접하는 느낌으로 모아놓은 것

environmentRecord와 호이스팅

environmentRecord

  • 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장
    • 컨텍스트를 구성하는 함수에 지정된 매개변수 식별자
    • 선언된 함수가 잇을 경우, 그 함수 자체
    • var로 선언된 변수의 식별자
  • 컨텍스트 내부 전체를 처음부터 끝까지 쭉 훑어나가며 순서대로 수집
  • 전역 실행 컨텍스트
    • 변수 객체를 생성하는 대신 자바스크립트 구동 환경이 별도로 제공하는 객체
    • 전역 객체에는 브라우저의 window, Node.js의 global 객체 등
    • 자바스크트립트 내장 객체(native object) ❌ / 호스트 객체(host object)로 분류 ⭕️

호이스팅(hoistiong)

  • hoistiong : ‘끌어올리다’의 hoist에 ing를 붙여 만든 동명사 -> 식별자들을 최상단으로 끌어 올려 놓자
  • 변수 정보를 수입하는 과정을 더욱 이해하기 쉬운 방법으로 대체한 가상의 개념
  • 자바스크립트 엔진이 실제로 끌어올리지는 않지만 편의상 끌어올린 것으로 간주

🚀 매개변수와 변수에 대한 호이스팅(원본)

function a(x) {
  // 수집 대상 1(매개변수)
  console.log(x); // (1)
  var x; // 수집 대상 2(변수 선언)
  console.log(x); // (2)
  var x = 2; // 수집 대상 3(변수 선언)
  console.log(x); //(3)
}
a(1);
  • 호이스팅 개념을 모를 시, 예상 값
    • (1) : 함수 호출 시 전달한 1 출력
    • (2) : 선언된 변수 x에 할당된 값이 없으므로 undefined 출력
    • (3) : 변수 x에 할당된 2 출력
  • 결과

🚀 매개변수와 변수에 대한 호이스팅 - 매개변수를 변수 선언/할당과 같다고 간주하여 변환한 상태

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

🚀 매개변수와 변수에 대한 호이스팅 - 호이스팅

function a() {
  var x; // 수집 대상 1의 변수 선언 부분
  var x; // 수집 대상 2의 변수 선언 부분
  var x; // 수집 대상 3의 변수 선언 부분

  x = 1; // 수집 대상 1의 할당 부분
  console.log(x); // (1)
  console.log(x); // (2)
  x = 2; // 수집 대상 3의 할당 부분
  console.log(x); // (3)
}
a(1);
  • 결과

🚀 함수 선언의 호이스팅 - 원본

function a() {
  console.log(b); // (1)
  var b = "bbb"; // 수집 대상1 (변수 선언)
  console.log(b); // (2)
  function b() {} // 수집 대상2 (함수 선언)
  console.log(b); // (3)
}
a();

🚀 함수 선언의 호이스팅 - 호이스팅

function a() {
  var b; // 수집 대상 1. 변수는 선언부만 끌어올린다.
  function b() {} // 수집 대상 2. 함수 선언은 전체를 끌어올린다.

  console.log(b); // (1)
  b = "bbb"; // 변수 할당부는 원래 자리에 남겨둔다.
  console.log(b); // (2)
  console.log(b); // (3)
}
a();

🚀 함수 선언의 호이스팅 - 함수 선언문을 함수 표현식으로 바꾼 코드

function a() {
  var b;
  var b = function b() {}; // 바뀐 부분

  console.log(b); // (1)
  b = "bbb";
  console.log(b); // (2)
  console.log(b); // (3)
}
a();
  • 결과

함수 선언문과 함수 표현식

  • 함수 선언문 : function 정의부만 존재하고 별도의 할당 명령이 없는 것.전체를 호이스팅 함.
    • 반드시 함수명이 정의돼 있어야 함
  • 함수 표현식 : 정의한 function을 별도의 변수에 할당 하는 것

    • 기명 함수 표현식 : 함수명을 정의한 함수 표현식
    • 익명 함수 표현식 : 정의하지 않은 것(일반)
  • 함수를 정의하는 세 가지 방식
function a() {
  /* ... */
} // 함수 선언문. 함수명 a가 곧 변수명
a(); // 실행 OK

var b = function () {
  /* ... */
}; // (익명) 함수 표현식. 변수명 b가 곧 함수명.
b(); // 실행 OK

var c = function d() {
  /* ... */
}; // 기명 함수 표현식. 변수명은 c. 함수명은 d.
c(); // 실행 OK
d(); // 에러

함수 선언문과 함수 표현식의 실질적인 차이

//원본코드
console.log(sum(1, 2));
console.log(multiply(3, 4));

function sum(a, b) {
  // 함수 선언문 sum
  return a + b;
}

var multiply = function (a, b) {
  // 함수 표현식 multiply
  return a * b;
};

//호이스팅
var sum = function sum(a, b) {
  //함수선언문 -> 함수 표현식으로 변환
  return a + b;
};
var multiply; // 변수는 선언부만 끌어올림
console.log(sum(1, 2));
console.log(multiply(3, 4));

multiply = function (a, b) {
  // 변수의 할당부는 원래 자리에 남김
  return a * b;
};

🔥 함수 선언문은 함수선언, 초기화, 할당을 한번에 진행 🔥 함수 표현식은 함수 호이스팅이 아닌 변수 호이스팅을 진행하여 변수 생성 및 초기화와 할당이 분리되어 진행

💡 함수 선언문보다 상대적으로 함수 표현식이 안전하다.

스코프, 스코프 체인, outerEnvironmentReference

스코프(Scope)

  • 식별자에 대한 유효범위
  • 함수스코프 : ES5에서는 함수에 의해서만 스코프 생성 -> var
  • 블록스코프 : ES6에서는 블록에 의해서도 스코프 경계 발생 -> let, const, class, strict mode

스코프 체인(Scope Chain)

  • ‘식별자의 유효범위’를 안에서부터 바깥으로 차례로 검색해나가는 것
  • 이를 가능케 하는 것이 바로 LexicalEnvironment의 두 번째 수집 자료인 outerEnvironmentReference

변수 은닉화(variable shadowing)

  • 상위 스코프에 선언되어 있지만, 현재 스코프에서 이미 선언된 경우네는 값이 할당되어 있지 않다고 하더라고 현재 스코프의 변수를 우선으로 한다는 규칙

전역변수와 지역변수

  • 전역변수(global variable) : 전역 공간에서 선언한 변수.
  • 지역변수(local variable) : 함수 내부에서 선언한 변수.

참고 : 코어 자바스크립트(Core JavaScript), 정재남