Lined Notebook

실행 컨텍스트

by yjym33

실행 컨텍스트란?

 

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

 

여기서 '동일한 환경' 즉 하나의 실행 컨텍스트를 구성할수 있는 방법으로

  • 전역공간
  • eval() 함수
  • 함수

 

가 있으며 자동으로 생성되는 전역공간과 사용하는 것을 추천하지 않는 eval() 함수를 제외하면 우리가 흔히 실행 컨텍스트를 구성하는 방법은 함수를 실행하는 것뿐입니다.

 

※ ES6에서는 블록 {}에 의해서도 새로운 실행 컨텍스트가 생성
// ------------------------------ (1)
var a = 1;
function outer() {
    function inner() {
        console.log(a);
        var a = 3;
    }
    inner(); // ----------------- (2)
    console.log(a);
}
outer(); // --------------------- (3)
console.log(a);

위 코드를 보며 실행 컨텍스트가 콜 스택에 어떤 순서로 쌓이고 실행되는 지 살펴볼수 있습니다.
처음 자바스크립트 코드를 실행하는 순간

(1) 전역 컨텍스트가 콜 스택에 담깁니다. (3)에서 outer 함수를 호출하면 자바스크립트 엔진은 outer에 대한 환경 정보를 수집해서 outer 실행 컨텍스트를 생성한 후 콜 스택에 담습니다. 전역 컨텍스트와 관련된 코드의 실행을 중단하고 outer 실행 컨텍스트와 관련된 코드를 순차적으로 실행합니다. 다시 (2)에서 inner 함수의 실행 컨텍스트가 콜 스택의 가장 위에 담기면 outer 컨텍스트는 중단하고 inner 함수 내부 코드를 진행합니다. 실행이 종료되면 콜 스택에서 제거되고 그 다음 순서에 있는 코드를 이어서 진행합니다.

실행 컨텍스트와 콜 스택

어떤 실행 컨텍스트가 활성화될 때 자바스크립트 엔진은 해당 컨텍스트에 관련된 코드를 실행하는 데 필요한 환경 정보들을 수집해서 실행 컨텍스트 객체에 저장합니다. 여기에 담기는 정보들은 다음과 같습니다.

 

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

활성화된 실행 컨텍스트의 수집 정보

 

Variable Environment

 

VariableEnvironment에 담기는 내용은 LexicalEnvironment와 같지만 최초 실행 시의 스냅샷을 유지한다는 점이 다릅니다.

 

VariableEnvironment에 대해 요약하면 다음과 같습니다.

  • 실행 컨텍스트를 생성할 때 VariableEnvironment에 정보를 담는다.
  • 담긴 정보를 복사해서 LexicalEnvironment를 만들고 활용한다.
  • VariableEnvironment와 LexicalEnvironment의 내부는 environmentRecord와 outer-EnvironmentReference로 구성되어 있다.

 

LexicalEnvironment

 

  • 자바스크립트 코드에서 변수나 함수 등의 식별자를 정의하는데 사용하는 객체.
  • VariableEnvironment와 달리 함수 실행 도중에 변경되는 사항이 즉시 반영됨.
  • 식별자와 참조 혹은 값을 기록하는 Environment Record outerEnvironmentReference 라는 또 다른 Lexical Environment를 참조하는 포인터로 구성된다.
  • outerEnvironmentReference 는 외부 Lexical Environment를 참조하는 포인터로, 중첩된 자바스크립트 코드에서 스코프 탐색을 하기 위해 사용한다.

 

   EnvironmentRecord

  • 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장됨.
  • 컨텍스트를 구성하는 함수에 지정됨 매개변수 식별자, 선언한 함수가 있을 경우 그 함수 자체, var로 선언된 변수의 식별자 등이 식별자에 해당함.
  • 컨텍스트 내부 전체를 처음부터 끝까지 쭉 훑어나가며 순서대로 수집 함. (호이스팅)

 

호이스팅

  • 변수 정보를 수집하는 과정은 모두 마쳤더라도 아직 실행 컨텍스트가 관여할 코드들은 실행되기 전의 상태 ⇒ 코드가 실행되기 전임에도 불구하고 자바스크립트 엔진은 이미 해당 환경에 속한 코드의 변수명들을 모두 알고 있음
  • 자바스크립트 엔진은 식별자들을 최상단으로 끌어올려놓은 다음 실제 코드를 실행한다 해도 코드를 해석하는데 문제가 없을 것 ⇒ 호이스팅
  • 호이스팅 ⇒ 변수 정보를 수집하는 과정을 더욱 이해하기 쉬운 방법으로 대체한 가상의 개념 (자바스크립트 엔진이 실제로 끌어올리지는 않음)

 

샘플 코드 (호이스팅 전)

function a() {
  var x = 1; // 매개변수
  console.log(x);
  var x;
  console.log(x);
  var x = 2;
  console.log(x);
}
a();

 

샘플 코드 (호이스팅 진행 중 - 매개변수를 변수 선언/할당과 같다고 간주해서 변환한 상태)

function a() {
  var x = 1; // 매개변수
  console.log(x);
  var x;
  console.log(x);
  var x = 2;
  console.log(x);
}
a();

 

샘플 코드(호이스팅 완료)

function a() {
  var x;
  var x;
  var x;

  x = 1; // 매개변수
  console.log(x); // 1
  console.log(x); // 1
  x = 2;
  console.log(x); // 2
}
a();

 

함수 선언문과 함수 표현식

 

함수 선언문 ⇒ function 정의부만 존재하고 별도의 할당 명령이 없는 것을 의미한다.

function a() { // 함수 선언문
	...
}

 

함수 표현식

  • 정의한 function을 별도의 변수에 할당하는 것을 의미한다.
const a = function() { // (익명) 함수 표현식
	...
}

  

함수를 정의하는 세 가지 방식

function a () { /* ... */ } 	// 함수 선언문. 함수명 a가 곧 변수명
a(); // 실행 OK

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

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

 

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

 

  • 샘플 코드 (호이스팅 전)
console.log(sum(1, 2));
console.log(multiply(3, 4));

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

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

 

  • 호이스팅 진행 후
var sum = function sum(a, b) {
  return a + b;
};
var multiply;

console.log(sum(1, 2)); // 3
console.log(multiply(3, 4)); // multiply is not a function

multiply = function (a, b) {
  return a * b;
};

->  함수 선언문은 전체를 호이스팅 한 반면 함수 표현식은 변수 선언부만 호이스팅 함. (디버깅 하기 쉬운 이유로 함수 표현식을 사용하는 것을 추천함)

 

원할한 협업을 위해서는 전역공간에 함수를 선언하거나 동명의 함수를 중복 선언하는 경우는 없어야만 하며. 

만에 하나 전역공간에 동명의 함수가 여럿 존재하는 상황이라 하더라도 모든 함수가 표현식으로 정의돼 있었다면 위와 같은 상황은 일어나지 않으므로 함수표현식을 사용하는 것을 추천하는 것입니다.

 

스코프

  • 식별자에 대한 유효범위.
  • 어떤 경계 A의 외부에서 선언한 변수는 A의 외부 뿐만 아니라, A의 내부에서도 접근이 가능하지만, A의 내부에서 선언한 변수는 오직 A의 내부에서만 접근할 수 있음.
const a = 10;

function b() {
  console.log(a); // a에 접근할 수 있음.
}

function c() {
  const d = 10;
}
console.log(d); // 접근할 수 없음. 오직 c() 내부에서만 접근 가능

 

스코프 체인

  • 이러한 식별자의 유효범위를 안에서 바깥으로 차례로 검색해나가는 것.
  • 스코프 체인을 가능하게 하는 것이 바로 LexcialEnvironment의 두 번째 수집 자료인 outerEnvironmentReference.

 

OuterEnvironmentReference

  • outerEnvironmentReference는 현재 호출된 함수가 선언될 당시의 LexcialEnviroment를 참조함.
  • 예를 들어 A 함수 내부에 B 함수를 선언하고, 다시 B 함수 내부에 C 함수를 선언한 경우, 함수 C의 outerEnvironmentReference는 함수 B의 LexicalEnvironment를 참조 함. 함수 B의 LexicalEnvironment에 있는 outerEnvironmentReference는 다시 함수 B가 선언되던 때(A)의 LexicalEnvironment를 참조.
  • 이러한 식으로 outerEnvironmentReference는 연결리스트 형태를 띰.
  • 각 outerEnvironmentReference는 오직 자신이 선언된 시점의 LexicalEnvironment만 참조하고 있으므로 가장 가까운 요소부터 차례대로 접근할 수 있고 다른 순서로 접근하는 것은 불가능 함 -> 이러한 특징으로 여러 스코프에서 동일한 식별자를 선언한 경우에는 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근 가능.
  • 참고로, 전역 컨텍스트의 LexicalEnvironment까지 탐색해도 해당 변수를 찾지 못하면 undefined를 반환.

 

변수 은닉화

  • 스코프 체인 상에 있는 변수라고 해서 무조건 접근 가능한 것은 아닙니다.
var a = 10;
function b() {
  var a = 20;
  console.log(a);
  // b() 내부에서 a에 접근하려고 하면 무조건 스코프 체인 상의 첫번째 인자부터 검색할 수 밖에 없음.
}

 

전역변수와 지역변수

  • 전역공간에 선언한 변수 ⇒ 전역 변수
  • 함수 내부에서 선언한 변수 ⇒ 지역 변수
  • 코드의 안전성을 위해 가급적 전역변수 사용을 최소화하고자 노력하는 것이 좋다!

 

ThisBinding

  • 실행 컨텍스트의 thisBinding에는 this로 지정된 객체가 저장됨.
  • 실행 컨텍스트 활성화 당시에 this가 지정되지 않은 경우 this에는 전역 객체가 저장됨.

'Development > Javascript' 카테고리의 다른 글

이벤트  (0) 2023.09.20
클로저  (0) 2023.09.20
프로토타입  (0) 2021.08.04
함수  (0) 2021.08.03
객체와 변경불가성(Immutability)  (0) 2021.08.02

블로그의 정보

생각보다 실천을

yjym33

활동하기