[코어 자바스크립트] 2장 실행 컨텍스트

woo·2023년 3월 7일
0
post-thumbnail

2-1 실행 컨텍스트란?

실행 컨텍스트는 실행할 코드에 제공할 환경정보를 모아놓은 객체이다.
동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보를 모아 컨텍스트를 구성하고, 이를 콜 스택에 쌓아 올렸다가, 가장 위에 쌓여있는 컨텍스트와 관련 있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장한다.
컨텍스트는 함수를 실행하거나 블록에 의해서 생성된다.

// -------------------------- (1)
let a = 1;
function outer() {
  function inner() {
    console.log(a); // undefined
    let a = 3;
  }
  inner(); // ------------ (2)
  console.log(a); // 1
}

outer(); // ---------------- (3)
console.log(a); // 1

위 코드가 실행될때, 처음 자바스크립트 코드가 실행될때는 전역 컨텍스트가 콜스택에 담긴다. 그 후 outer(), inner() 함수가 차례대로 콜스택에 담긴다. inner(), outer(), 전역 컨텍스트 순서대로 실행되게 된다.

실행 컨텍스트 객체는 VariableEnvironment, LexicalEnvironment, this 바인딩으로 구성되어 있다.


실행 컨텍스트 구조 사진 출처

그리고 VariableEnvironment, LexicalEnvironment는 각각 Environment Record와 Outer Environment Reference로 구성된다.

2-2 VariableEnvironment

실행 컨텍스트를 생성할 때, VariableEnvironment에 정보를 먼저 담은 다음, 이를 복사해서 LexicalEnvironment를 만든다. 이후에는 LexicalEnvironment를 활용한다.

VariableEnvironment와 LexicalEnvironment는 동일한 내용을 구성되지만, VariableEnvironment는 초기 상태를 유지하고 LexicalEnvironment 는 함수 실행 도중 변경되는 사항을 즉시 반영한다.

2-3 LexicalEnvironment

LexicalEnvironment에는 컨텍스트를 구성하는 환경 정보에 대해서 저장한다.

2-3-1 environmentRecord와 호이스팅

environmentRecord에는 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다. 예를 들어 선언된 함수, 함수에 지정된 매변수 식별자, 변수의 식별자 등이 저장된다. 컨텍스트 내부 전체를 처음부터 끝까지 훑어나가며 순서대로 수집한다.

이렇게 변수를 수집하는 과정은 코드가 실행되기 전에 일어난다. "자바스크립트 엔진은 식별자들을 최상단으로 끌어올려놓은다음 실제 코드를 실행"하는 호이스팅 개념이 등장하게 된다.

호이스팅 예제 1️⃣

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

위의 코드는 아래와 같이 호이스팅이 이루어진다.

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

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

(1) = 1, (2) = 1, (3) = 2 의 결과를 출력하게 된다.

호이스팅 예제 2️⃣

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

위의 코드는 아래와 같이 호이스팅이 이루어진다.

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

  console.log(b); // (1)
  b = 'bbb';
  console.log(b); // (2)
  console.log(b); // (3)
}
a();

(1) = 함수 b, (2) = 'bbb', (3) = 'bbb' 의 결과를 출력하게 된다.

함수 선언문은 호이스팅이 되고 함수 표현식은 할당 부분은 해당 코드 줄 이후에만 영향을 끼친다.

console.log(sum(1, 2));
console.log(multiply(3, 4));

function sum(a, b) {
  // 함수 선언문 sum은 호이스팅
  return a + b;
}

// multiply 변수 선언문만 호이스팅
let multiply = function(a, b) {
  // 함수 표현식 multiply에서 할당문인 함수는 호이스팅이 되지 않음
  return a * b;
};

함수 선언문을 잘못 사용하면 앞서 선언한 선언들을 덮어 씌워 예상치 못한 오류를 발생시킬 수 있다.
따라서 함수 표현식으로, 화살표 함수 () => {}를 사용하는 것이 안전하다.

2-3-2 스코프, 스코프 체인, outerEnvironmentReference

스코프란 식별자에 대한 유효범위이다. 식별자에 대해 접근가능한 범위를 뜻한다.
"식별자에 대한 유효범위"를 안에서 바깥으로 차례로 검색해나아가는 것을 스코프 체인이라고 부르며 이를 가능하게 하는 것이 outerEnvironmentReference이다.

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

예를 들어, A 함수 내부에 B 함수를 선언하고 다시 B 함수 내부에 C 함수를 선언한 경우, 함수의 outerEnvironmentReference는 함수 B의 LexicalEnvironment를 참조한다. 함수 B의 LexicalEnvironment에 있는 outerEnvironmentReference는 다시 함수 B가 선언되던 때(A)의 LexicalEnvironment를 참조한다.
이처럼 outerEnvironmentReference는 연결리스트(linked list) 형태로 이어져 있어 '선언 시점의 LexicalEnvironment' 를 계속 찾아 올라가면 마지막엔 전역 컨텍스트의 LexicalEnvironment에 도달한다.
여러 스코프에서 동일한 식별자를 선언한 경우에는 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근 가능하다.


스코프 체인 사진 출처

전역 컨텍스트의 LexicalEnvironment에 담긴 변수를 전역변수라고 하며, 그 밖의 함수에 의해 생성된 실행 컨텍스트의 변수들은 지역변수이다.

  • 변수 은닉화 : 전역변수와 동일한 이름의 지역변수를 선언하여 스코프 체인이 지역변수에 접근되게 한다.
profile
🌱 매일 성장하는 개발자

0개의 댓글

관련 채용 정보