Be lazy, Be crazy

2. 실행 컨텍스트 본문

책/코어 자바스크립트

2. 실행 컨텍스트

LAZY SIA 2021. 12. 29. 11:15
728x90
반응형

 

1-1 실행 컨텍스트란?

스택과 큐

1) 스택(Stack)

- 출입구가 하나뿐인 깊은 우물 같은 데이터 구조

- 후입선출(LIFO)

 

2) 큐(Queue)

- 양쪽이 모두 열려있는 파이프 같은 데이터 구조

- 선입선출(FIFO)

 

 

실행 컨텍스트

- 실행할 코드에 제공할 환경 정보들을 모아놓은 객체

- 자바스크립트의 동적 언어로서의 성격을 가장 잘 파악할 수 있는 개념

 

- 동일한 환경*에 있는 코드 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성

   (*동일한 환경: 하나의 실행 컨텍스트를 구성할 수 있는 방법으로 전역공간, eval() 함수, 함수 등이 있음)

- 이를 콜 스택에 쌓아올렸다가 가장 위에 쌓여있는 컨텍스트와 관련 있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장

- 자동으로 생성되는 전역공간과 악마로 취급받는 eval을 제외하면 우리가 흔히 실행 컨텍스트를 구성하는 방법은 함수를 실행하는 것뿐

   (ES6에서는 블록에 의해서도 새로운 실행 컨텍스트가 생성)

 

 

실행 컨텍스트가 콜 스택에 쌓이는 순서 (후입선출)

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

 

자바스크립트 파일이 열리는 순간(1) : 전역 실행 컨텍스트 콜 스택에 맨 위에 쌓임 -> 전역 코드 순차 실행

outer 함수 호출되는 순간(2) : outer 실행 컨텍스트 콜 스택에 맨 위에 쌓임 -> 전역 컨텍스트 일시중단, outer 함수 내부 코드 순차 실행

inner 함수 호출되는 순간(3) : inner 실행 컨텍스트 콜 스택에 맨 위에 쌓임 -> outer 컨텍스트 일시중단, inner 함수 내부 코드 순차 실행

inner 함수 실행 종료 : inner 실행 컨텍스트 콜 스택에서 제거 -> 중단되었던 outer 컨텍스트 이어서 실행

outer 함수 실행 종료 : outer 실행 컨텍스트 콜 스택에서 제거 -> 중단되었던 전역 컨텍스트 이어서 실행

전역 공간의 모든 코드 실행 종료 : 전역 실행 컨텍스트 콜 스택에서 제거 -> 콜스택에는 아무것도 남지 않은 상태로 종료

 

- 한 실행 컨텍스트가 콜 스택의 맨 위에 쌓이는 순간이 곧 현재 실행할 코드에 관여하게 되는 시점

- 어떤 실행 컨텍스트가 활성화될 때 자바스크립트 엔진은 해당 컨텍스트에 관련된 코드들을 실행하는 데 필요한 환경 정보들을 수집해서 실행 컨텍스트 객체에 저장

 

 

실행 컨텍스트 객체에 담기는 정보

- VariableEnvironment : 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보. 선언 시점의 LexicalEnvironment의 스냅샷. 변경 사항 반영되지 않음

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

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


1-2 VariableEnvironment

LexicalEnvironment와의 차이점

- 담기는 내용은 같지만 VariableEnvironment는 최초 실행 시의 스냅샷을 유지한다는 점이 다름

 

 

실행 컨텍스트 생성될 때 순서

①VariableEnvironment에 정보 담음 -> ②그대로 복사해서 LexicalEnvironment 만듬 -> ③LexicalEnvironment 주로 활용

 

- VariableEnvironment와 LexicalEnvironment의 내부는 'environmentRecord'와 'outer-EnvironmentReference'로 구성됨

- 초기화 과정 중엔 사실상 완전히 동일하고 이후 코드 진행에 따라 서로 달라지게 됨


1-3 LexicalEnvironment

- 컨텍스트를 구성하는 환경 정보들을 사전에서 접하는 느낌으로 모아놓은 것

environmentRecord와 호이스팅(hoisting)

environmentRecord

- 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장(매개변수 이름, 함수 선언, 변수명 등)

- 컨텍스트 내부 전체를 처음부터 끝까지 쭉 훑어나가며 순서대로 수집(코드 실행 전 정보만 수집)

- 코드가 실행되기 전에도 자바스크립트 엔진은 이미 변수명들을 모두 알고 있게 됨

-> '자바스크립트 엔진은 식별자들을 최상단으로 끌어올려놓은 다음 실제 코드를 실행한다'

-> 호이스팅 개념 등장

 

<참고>

전역 실행 컨텍스트는 변수 객체를 생성하는 대신 자바스크립트 구동 환경이 별도로 제공하는 전역 객체를 활용

(전역 객체에는 브라우저의 window, Node.js의 global 객체 등이 있음. 이들은 내장 객체가 아닌 호스트 객체로 분류)

 

 

호이스팅(hoisting)

변수 정보를 수집하는 과정을 더욱 이해하기 쉬운 방법으로 대체한 가상의 개념

 

 

호이스팅 규칙

1) 매개변수와 변수에 대한 호이스팅 예시 (더보기 클릭)

더보기

원본 코드

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);

 

매개변수를 변수 선언/할당과 같다고 간주해서 변환한 상태

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);

 

2) 함수 선언의 호이스팅 예시 (더보기 클릭)

더보기

원본 코드

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(); // 실행 ok

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

// 기명 함수 표현식. 변수명 ≠ 함수명
var c = function 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;
};

 

함수 선언문의 위험성

// ...
console.log(sum(3, 4));
// ...

function sum (x, y) {
    return x + y;
}

// ...
var a = sum(1, 2);
// ...

function sum (x, y) {
    return x + '+' + y + '=' + (x+y);
}

// ...
var c = sum(1, 2);
console.log(c);
// ...

- 코드를 실행하는 중에 실제로 호출되는 함수는 오직 마지막에 할당한 함수, 즉 맨 마지막에 선언된 함수 뿐 

 

상대적으로 함수 표현식이 안전하다

// ...
console.log(sum(3, 4));	// Uncaught Type Error: sumb is not a function
// ...

var sum = function (x, y) {
    return x + y;
};

// ...
var a = sum(1, 2);
// ...

var sum = function (x, y) {
    return x + '+' + y + '=' + (x+y);
};

// ...
var c = sum(1, 2);
console.log(c);
// ...

 

스코프, 스코프 체인, outerEnvironmentReference

스코프

- 식별자에 대한 유효범위

- ES5까지의 자바스크립트는 전역공간을 제외하면 오직 함수에 의해서만 스코프가 생성

 

스코프 체인

- '식별자의 유효범위(스코프)'를 안에서부터 바깥으로 차례로 검색해나가는 것

- 이를 가능케 하는 것이 바로 LexicalEnvironment의 두 번째 수집 자료인 outerEnvironmentReference

 

outerEnvironmentReference

- 현재 호출된 함수가 선언될 당시의 LexicalEnvironment를 참조

- '선언하다'라는 행위가 실제로 일어날 수 있는 시점이란 콜 스택 상에서 어떤 실행 컨텍스트가 활성화된 상태일 때뿐

   (어떤 함수를 선언하는 행위 자체도 하나의 코드가 지나지 않으며, 모든 코드는 실행 컨텍스트가 활성화 상태일 때 실행되기 때문)

 

전역변수와 지역변수

- 전역변수 : 전역 공간에서 선언한 변수

- 지역변수 : 함수 내부에서 선언한 변수

- 코드의 안정성을 위해 가급적 전역변수 사용을 최소화하는 것이 좋음


1-4 this

- 실행 컨텍스트의 thisBinding에는 this로 지정된 객체가 저장됨

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

- 그밖에는 함수를 호출하는 방법에 따라 this에 저장되는 대상이 다름

반응형
Comments