본문 바로가기
JavaScript

[JavaScript] 실행컨텍스트(스코프, 변수, 객체 호이스팅)

by junvely 2023. 4. 5.

실행 컨텍스트

자바스크립트의 실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체다. 자바스크립트는 어떤 실행 컨텍스트가 활성화되는 시점에 record(hoisting), outer, this값을 설정 한다.

1) 콜 스택

코드를 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고 이것을 위에서 설명드린 ‘스택’의 한 종류인 콜스택에 쌓아올린다. 가장 위에 쌓여있는 컨텍스트와 관련된 코드를 실행하는 방법으로 코드의 환경 및 순서를 보장할 수 있다.

 

 

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

1. VE => VariableEnvironment

1) 현재 컨텍스트 내의 식별자 정보(=record)를 갖고있다.

  1. var a = 3의 경우, **var a**를 의미, 선언부
  2. 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장.

2) 외부 환경 정보(outer)를 갖고있다.

3) 선언 시점 LexicalEnvironment의 snapshot을 유지한다.

 

2. LE => LexicalEnvironment  => record와 호이스팅, outer, this

1) VariableEnvironment와 생성 당시 동일하지만, 변경사항을 실시간으로 반영한다.

2) record와 호이스팅

- 현재 컨텍스트와 관련된 코드의 식별자 정보들을 호이스팅하여 저장(수집)된다.

- 수집 대상 정보 : 함수에 지정된 매개변수 식별자, 함수 자체, var로 선언된 변수 식별자

호이스팅 법칙 1 : 매개변수 및 변수는 선언부를 호이스팅 

호이스팅 법칙 2 : 함수 선언은 전체를 호이스팅 => 함수 선언문과 표현식이 다름(표현식일 경우 변수 선언부만 호이스팅)

// 기존 코드
function a () {
	console.log(b);
	var b = 'bbb';
	console.log(b);
	function b() { }
	console.log(b);
}
a();
// 호이스팅
function a () {
	var b; // 변수 선언부 호이스팅
	function b() { } // 함수 선언은 전체를 호이스팅

	console.log(b);
	b = 'bbb'; // 변수의 할당부는 원래 자리에

	console.log(b);
	console.log(b);
}
a();

- 함수 선언식과 표현식의 차이

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;
}
// 함수 선언문은 전체를 hoisting
function sum (a, b) { // 함수 선언문 sum
	return a + b;
}

// 변수는 선언부만 hoisting

var multiply; 

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

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

- 선언식보다 표현식을 권장하는 이유 => 기존 코드가 호이스팅 되며 덮어쓸 수 있음, 예측하기 어려운 결과

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

// 함수 선언문으로 짠 코드
// 100번째 줄 : 시니어 개발자 코드(활용하는 곳 -> 200군데)
// hoisting에 의해 함수 전체가 위로 쭉!
function sum (x, y) {
	return x + y;
}

...
...

var a = sum(1, 2);

...

// 함수 선언문으로 짠 코드
// 5000번째 줄 : 신입이 개발자 코드(활용하는 곳 -> 10군데)
// hoisting에 의해 함수 전체가 위로 쭉!
function sum (x, y) {
	return x + ' + ' + y + ' = ' + (x + y);
}

...

var c = sum(1, 2);

console.log(c);

 

🔥정리 )

- 실행 컨텍스트 : 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.

- 그 객체 안에는 3가지가 존재한다.
   ✓ VariableEnvironment
   ✓ LexicalEnvironment
   ✓ ThisBindings
   
- VE와 LE는 실행컨텍스트 생성 시점에 내용이 완전히 같고, 이후 스냅샷 유지 여부가 다르다.
- LE는 다음 2가지 정보를 가지고 있다.
   ✓ record(=environmentRecord) ← 이 record의 수집과정이 hoisting
   ✓ outer(=outerEnvironmentReference)

 

3) outer => 실행 컨텍스트가 만들어질 때의 환경정보 => 스코프 체인이 가능하도록 하는 것(*외부 참조 정보)

1-1) 스코프 : 식별자에 대한 유효범위를 의미

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

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

- 예를 들어, A함수 내부에 B함수 선언 → B함수 내부에 C함수 선언한 경우 => 결국 타고, 타고 올라가다 보면 전역 컨텍스트의 LexicalEnvironment를 참조하게 된다.

var a = 1;
var outer = function() {
	var inner = function() {
		console.log(a); // 이 값은 뭐가 나올지 예상해보세요! 이유는 뭐죠? scope 관점에서!
		var a = 3;  // undeifined => 선언부만 호이스팅 됨
	};
	inner();
	console.log(a); // 이 값은 또 뭐가 나올까요? 이유는요? scope 관점에서!
    // 1 => outer는 전역의 LE를 참조하여 outer 외부 환경을 가지고 있기 때문
};
outer();
console.log(a); // 이 값은 뭐가 나올까요? 마찬가지로 이유도!
// 1

🔥정리 )

*현재 실행 컨텍스트가 만들어질 시점에, 최상위(이전 컨텍스트) 컨텍스트의 LE를 참조하여 outer 외부환경으로 가지고 있는다.

C의 실행 컨텍스트가 만들어질 때, B의 LE를 참조하여 외부 환경으로 outer로 가지고 있음

B의 실행 컨텍스트가 만들어질 때, A의 LE를 참조하여 외부 환경으로 outer로 가지고 있음

A도 동일, 전역은 LE(record) outer를 가지고 있고, A는 전역의 LE(record)를 참조하여 외부 환경으로 outer로 생성함

*결과적으로 선언 당시 컨텍스트(최상위)의 record를 읽어 외부 환경 outer로 참조하면서, 각각의 실행 컨텍스트는 LE 안에 record와 outer를 가지고 있고, outer 안에는 그 실행 컨텍스트가 선언될 당시의 LE정보가 다 들어있으니 scope chain에 의해 상위 컨텍스트의 record를 읽어올 수 있다.

각각의 실행 컨텍스트는 LE 안에 record와 outer를 가지고 있고, 
outer 안에는 그 실행 컨텍스트가 선언될 당시의 LE정보가 다 들어있으니 
scope chain에 의해 상위 컨텍스트의 record를 읽어올 수 있다.

 

 

3. This Binding => this 식별자가 바라봐야할 객체

참조 : this binding

 

 

 

* 위 내용은 스파르타 코딩클럽 JavaScript 문법 종합반 강의 내용입니다.